Migrate VaultAddItemScreen to use a LazyColumn (#345)

This commit is contained in:
David Perez 2023-12-07 09:17:22 -06:00 committed by Álison Fernandes
parent a91de90fcd
commit 446b0f9da4
6 changed files with 596 additions and 468 deletions

View file

@ -0,0 +1,282 @@
package com.x8bit.bitwarden.ui.vault.feature.additem
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.components.BitwardenFilledTonalButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenFilledTonalButtonWithIcon
import com.x8bit.bitwarden.ui.platform.components.BitwardenIconButtonWithResource
import com.x8bit.bitwarden.ui.platform.components.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenPasswordFieldWithActions
import com.x8bit.bitwarden.ui.platform.components.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.components.BitwardenSwitchWithActions
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextFieldWithActions
import com.x8bit.bitwarden.ui.platform.components.model.IconResource
import kotlinx.collections.immutable.toImmutableList
/**
* The UI for adding and editing a login cipher.
*/
@Suppress("LongMethod")
fun LazyListScope.addEditLoginItems(
state: VaultAddItemState.ItemType.Login,
loginItemTypeHandlers: VaultAddLoginItemTypeHandlers,
) {
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextField(
label = stringResource(id = R.string.name),
value = state.name,
onValueChange = loginItemTypeHandlers.onNameTextChange,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextFieldWithActions(
label = stringResource(id = R.string.username),
value = state.username,
onValueChange = loginItemTypeHandlers.onUsernameTextChange,
actions = {
BitwardenIconButtonWithResource(
iconRes = IconResource(
iconPainter = painterResource(id = R.drawable.ic_generator),
contentDescription = stringResource(id = R.string.generate_username),
),
onClick = loginItemTypeHandlers.onOpenUsernameGeneratorClick,
)
},
modifier = Modifier.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenPasswordFieldWithActions(
label = stringResource(id = R.string.password),
value = state.password,
onValueChange = loginItemTypeHandlers.onPasswordTextChange,
modifier = Modifier
.padding(horizontal = 16.dp),
) {
BitwardenIconButtonWithResource(
iconRes = IconResource(
iconPainter = painterResource(id = R.drawable.ic_check_mark),
contentDescription = stringResource(id = R.string.check_password),
),
onClick = loginItemTypeHandlers.onPasswordCheckerClick,
)
BitwardenIconButtonWithResource(
iconRes = IconResource(
iconPainter = painterResource(id = R.drawable.ic_generator),
contentDescription = stringResource(id = R.string.generate_password),
),
onClick = loginItemTypeHandlers.onOpenPasswordGeneratorClick,
)
}
}
item {
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.authenticator_key),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
BitwardenFilledTonalButtonWithIcon(
label = stringResource(id = R.string.setup_totp),
icon = painterResource(id = R.drawable.ic_light_bulb),
onClick = loginItemTypeHandlers.onSetupTotpClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.ur_is),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextFieldWithActions(
label = stringResource(id = R.string.uri),
value = state.uri,
onValueChange = loginItemTypeHandlers.onUriTextChange,
actions = {
BitwardenIconButtonWithResource(
iconRes = IconResource(
iconPainter = painterResource(id = R.drawable.ic_settings),
contentDescription = stringResource(id = R.string.options),
),
onClick = loginItemTypeHandlers.onUriSettingsClick,
)
},
modifier = Modifier.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
BitwardenFilledTonalButton(
label = stringResource(id = R.string.new_uri),
onClick = loginItemTypeHandlers.onAddNewUriClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.miscellaneous),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenMultiSelectButton(
label = stringResource(id = R.string.folder),
options = state.availableFolders.toImmutableList(),
selectedOption = state.folder,
onOptionSelected = loginItemTypeHandlers.onFolderTextChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
BitwardenSwitch(
label = stringResource(
id = R.string.favorite,
),
isChecked = state.favorite,
onCheckedChange = loginItemTypeHandlers.onToggleFavorite,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
BitwardenSwitchWithActions(
label = stringResource(id = R.string.password_prompt),
isChecked = state.masterPasswordReprompt,
onCheckedChange = loginItemTypeHandlers.onToggleMasterPasswordReprompt,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
actions = {
IconButton(onClick = loginItemTypeHandlers.onTooltipClick) {
Icon(
painter = painterResource(id = R.drawable.ic_tooltip),
tint = MaterialTheme.colorScheme.onSurface,
contentDescription = stringResource(
id = R.string.master_password_re_prompt_help,
),
)
}
},
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.notes),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextField(
singleLine = false,
label = stringResource(id = R.string.notes),
value = state.notes,
onValueChange = loginItemTypeHandlers.onNotesTextChange,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.custom_fields),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
BitwardenFilledTonalButton(
label = stringResource(id = R.string.new_custom_field),
onClick = loginItemTypeHandlers.onAddNewCustomFieldClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.ownership),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenMultiSelectButton(
label = stringResource(id = R.string.who_owns_this_item),
options = state.availableOwners.toImmutableList(),
selectedOption = state.ownership,
onOptionSelected = loginItemTypeHandlers.onOwnershipTextChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
}
}

View file

@ -0,0 +1,170 @@
package com.x8bit.bitwarden.ui.vault.feature.additem
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.ui.platform.components.BitwardenFilledTonalButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.components.BitwardenSwitchWithActions
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextField
import kotlinx.collections.immutable.toImmutableList
/**
* The UI for adding and editing a secure notes cipher.
*/
@Suppress("LongMethod")
fun LazyListScope.addEditSecureNotesItems(
state: VaultAddItemState.ItemType.SecureNotes,
secureNotesTypeHandlers: VaultAddSecureNotesItemTypeHandlers,
) {
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextField(
label = stringResource(id = R.string.name),
value = state.name,
onValueChange = secureNotesTypeHandlers.onNameTextChange,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.miscellaneous),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenMultiSelectButton(
label = stringResource(id = R.string.folder),
options = state.availableFolders.map { it.invoke() }.toImmutableList(),
selectedOption = state.folderName.invoke(),
onOptionSelected = secureNotesTypeHandlers.onFolderTextChange,
modifier = Modifier
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
BitwardenSwitch(
label = stringResource(id = R.string.favorite),
isChecked = state.favorite,
onCheckedChange = secureNotesTypeHandlers.onToggleFavorite,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
BitwardenSwitchWithActions(
label = stringResource(id = R.string.password_prompt),
isChecked = state.masterPasswordReprompt,
onCheckedChange = secureNotesTypeHandlers.onToggleMasterPasswordReprompt,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
actions = {
IconButton(onClick = secureNotesTypeHandlers.onTooltipClick) {
Icon(
painter = painterResource(id = R.drawable.ic_tooltip),
tint = MaterialTheme.colorScheme.onSurface,
contentDescription = stringResource(
id = R.string.master_password_re_prompt_help,
),
)
}
},
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.notes),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextField(
singleLine = false,
label = stringResource(id = R.string.notes),
value = state.notes,
onValueChange = secureNotesTypeHandlers.onNotesTextChange,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.custom_fields),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
BitwardenFilledTonalButton(
label = stringResource(id = R.string.new_custom_field),
onClick = secureNotesTypeHandlers.onAddNewCustomFieldClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.ownership),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
BitwardenMultiSelectButton(
label = stringResource(id = R.string.who_owns_this_item),
options = state.availableOwners.toImmutableList(),
selectedOption = state.ownership,
onOptionSelected = secureNotesTypeHandlers.onOwnershipTextChange,
modifier = Modifier
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(24.dp))
}
}

View file

@ -15,7 +15,7 @@ fun NavGraphBuilder.vaultAddItemDestination(
onNavigateBack: () -> Unit,
) {
composable(
ADD_ITEM_ROUTE,
route = ADD_ITEM_ROUTE,
enterTransition = TransitionProviders.Enter.slideUp,
exitTransition = TransitionProviders.Exit.slideDown,
popEnterTransition = TransitionProviders.Enter.slideUp,

View file

@ -1,8 +1,6 @@
package com.x8bit.bitwarden.ui.vault.feature.additem
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@ -10,12 +8,8 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
@ -31,20 +25,11 @@ 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.BitwardenFilledTonalButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenFilledTonalButtonWithIcon
import com.x8bit.bitwarden.ui.platform.components.BitwardenIconButtonWithResource
import com.x8bit.bitwarden.ui.platform.components.BitwardenListHeaderText
import com.x8bit.bitwarden.ui.platform.components.BitwardenMultiSelectButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenPasswordFieldWithActions
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
import com.x8bit.bitwarden.ui.platform.components.BitwardenSwitch
import com.x8bit.bitwarden.ui.platform.components.BitwardenSwitchWithActions
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextField
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextFieldWithActions
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
import com.x8bit.bitwarden.ui.platform.components.model.IconResource
import kotlinx.collections.immutable.toImmutableList
/**
@ -58,7 +43,6 @@ fun VaultAddItemScreen(
viewModel: VaultAddItemViewModel = hiltViewModel(),
) {
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
val scrollState = rememberScrollState()
val context = LocalContext.current
EventsEffect(viewModel = viewModel) { event ->
@ -80,10 +64,8 @@ fun VaultAddItemScreen(
}
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
BitwardenScaffold(
modifier = Modifier
.imePadding()
.fillMaxSize()
.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
@ -106,32 +88,36 @@ fun VaultAddItemScreen(
)
},
) { innerPadding ->
Column(
LazyColumn(
modifier = Modifier
.imePadding()
.padding(innerPadding)
.fillMaxSize()
.verticalScroll(scrollState),
.fillMaxSize(),
) {
BitwardenListHeaderText(
label = stringResource(id = R.string.item_information),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
TypeOptionsItem(
selectedType = state.selectedType,
onTypeOptionClicked = remember(viewModel) {
{ typeOption: VaultAddItemState.ItemTypeOption ->
viewModel.trySendAction(VaultAddItemAction.TypeOptionSelect(typeOption))
}
},
modifier = Modifier.padding(horizontal = 16.dp),
)
item {
BitwardenListHeaderText(
label = stringResource(id = R.string.item_information),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
}
item {
Spacer(modifier = Modifier.height(8.dp))
TypeOptionsItem(
selectedType = state.selectedType,
onTypeOptionClicked = remember(viewModel) {
{ typeOption: VaultAddItemState.ItemTypeOption ->
viewModel.trySendAction(VaultAddItemAction.TypeOptionSelect(typeOption))
}
},
modifier = Modifier.padding(horizontal = 16.dp),
)
}
when (val selectedType = state.selectedType) {
is VaultAddItemState.ItemType.Login -> {
AddLoginTypeItemContent(
addEditLoginItems(
state = selectedType,
loginItemTypeHandlers = loginItemTypeHandlers,
)
@ -146,12 +132,16 @@ fun VaultAddItemScreen(
}
is VaultAddItemState.ItemType.SecureNotes -> {
AddSecureNotesTypeItemContent(
addEditSecureNotesItems(
state = selectedType,
secureNotesTypeHandlers = secureNotesTypeHandlers,
)
}
}
item {
Spacer(modifier = Modifier.navigationBarsPadding())
}
}
}
}
@ -179,340 +169,3 @@ private fun TypeOptionsItem(
modifier = modifier,
)
}
@Suppress("LongMethod")
@Composable
private fun ColumnScope.AddLoginTypeItemContent(
state: VaultAddItemState.ItemType.Login,
loginItemTypeHandlers: VaultAddLoginItemTypeHandlers,
) {
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextField(
label = stringResource(id = R.string.name),
value = state.name,
onValueChange = loginItemTypeHandlers.onNameTextChange,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextFieldWithActions(
label = stringResource(id = R.string.username),
value = state.username,
onValueChange = loginItemTypeHandlers.onUsernameTextChange,
actions = {
BitwardenIconButtonWithResource(
iconRes = IconResource(
iconPainter = painterResource(id = R.drawable.ic_generator),
contentDescription = stringResource(id = R.string.generate_username),
),
onClick = loginItemTypeHandlers.onOpenUsernameGeneratorClick,
)
},
modifier = Modifier.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
BitwardenPasswordFieldWithActions(
label = stringResource(id = R.string.password),
value = state.password,
onValueChange = loginItemTypeHandlers.onPasswordTextChange,
modifier = Modifier
.padding(horizontal = 16.dp),
) {
BitwardenIconButtonWithResource(
iconRes = IconResource(
iconPainter = painterResource(id = R.drawable.ic_check_mark),
contentDescription = stringResource(id = R.string.check_password),
),
onClick = loginItemTypeHandlers.onPasswordCheckerClick,
)
BitwardenIconButtonWithResource(
iconRes = IconResource(
iconPainter = painterResource(id = R.drawable.ic_generator),
contentDescription = stringResource(id = R.string.generate_password),
),
onClick = loginItemTypeHandlers.onOpenPasswordGeneratorClick,
)
}
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.authenticator_key),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(16.dp))
BitwardenFilledTonalButtonWithIcon(
label = stringResource(id = R.string.setup_totp),
icon = painterResource(id = R.drawable.ic_light_bulb),
onClick = loginItemTypeHandlers.onSetupTotpClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.ur_is),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextFieldWithActions(
label = stringResource(id = R.string.uri),
value = state.uri,
onValueChange = loginItemTypeHandlers.onUriTextChange,
actions = {
BitwardenIconButtonWithResource(
iconRes = IconResource(
iconPainter = painterResource(id = R.drawable.ic_settings),
contentDescription = stringResource(id = R.string.options),
),
onClick = loginItemTypeHandlers.onUriSettingsClick,
)
},
modifier = Modifier.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(16.dp))
BitwardenFilledTonalButton(
label = stringResource(id = R.string.new_uri),
onClick = loginItemTypeHandlers.onAddNewUriClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.miscellaneous),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
BitwardenMultiSelectButton(
label = stringResource(id = R.string.folder),
options = state.availableFolders.toImmutableList(),
selectedOption = state.folder,
onOptionSelected = loginItemTypeHandlers.onFolderTextChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(16.dp))
BitwardenSwitch(
label = stringResource(
id = R.string.favorite,
),
isChecked = state.favorite,
onCheckedChange = loginItemTypeHandlers.onToggleFavorite,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(16.dp))
BitwardenSwitchWithActions(
label = stringResource(id = R.string.password_prompt),
isChecked = state.masterPasswordReprompt,
onCheckedChange = loginItemTypeHandlers.onToggleMasterPasswordReprompt,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
actions = {
IconButton(onClick = loginItemTypeHandlers.onTooltipClick) {
Icon(
painter = painterResource(id = R.drawable.ic_tooltip),
tint = MaterialTheme.colorScheme.onSurface,
contentDescription = stringResource(
id = R.string.master_password_re_prompt_help,
),
)
}
},
)
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.notes),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextField(
singleLine = false,
label = stringResource(id = R.string.notes),
value = state.notes,
onValueChange = loginItemTypeHandlers.onNotesTextChange,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.custom_fields),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(16.dp))
BitwardenFilledTonalButton(
label = stringResource(id = R.string.new_custom_field),
onClick = loginItemTypeHandlers.onAddNewCustomFieldClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.ownership),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
BitwardenMultiSelectButton(
label = stringResource(id = R.string.who_owns_this_item),
options = state.availableOwners.toImmutableList(),
selectedOption = state.ownership,
onOptionSelected = loginItemTypeHandlers.onOwnershipTextChange,
modifier = Modifier.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.navigationBarsPadding())
}
@Suppress("LongMethod")
@Composable
private fun ColumnScope.AddSecureNotesTypeItemContent(
state: VaultAddItemState.ItemType.SecureNotes,
secureNotesTypeHandlers: VaultAddSecureNotesItemTypeHandlers,
) {
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextField(
label = stringResource(id = R.string.name),
value = state.name,
onValueChange = secureNotesTypeHandlers.onNameTextChange,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.miscellaneous),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
BitwardenMultiSelectButton(
label = stringResource(id = R.string.folder),
options = state.availableFolders.map { it.invoke() }.toImmutableList(),
selectedOption = state.folderName.invoke(),
onOptionSelected = secureNotesTypeHandlers.onFolderTextChange,
modifier = Modifier
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(16.dp))
BitwardenSwitch(
label = stringResource(id = R.string.favorite),
isChecked = state.favorite,
onCheckedChange = secureNotesTypeHandlers.onToggleFavorite,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(16.dp))
BitwardenSwitchWithActions(
label = stringResource(id = R.string.password_prompt),
isChecked = state.masterPasswordReprompt,
onCheckedChange = secureNotesTypeHandlers.onToggleMasterPasswordReprompt,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
actions = {
IconButton(onClick = secureNotesTypeHandlers.onTooltipClick) {
Icon(
painter = painterResource(id = R.drawable.ic_tooltip),
tint = MaterialTheme.colorScheme.onSurface,
contentDescription = stringResource(
id = R.string.master_password_re_prompt_help,
),
)
}
},
)
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.notes),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
BitwardenTextField(
singleLine = false,
label = stringResource(id = R.string.notes),
value = state.notes,
onValueChange = secureNotesTypeHandlers.onNotesTextChange,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.custom_fields),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(16.dp))
BitwardenFilledTonalButton(
label = stringResource(id = R.string.new_custom_field),
onClick = secureNotesTypeHandlers.onAddNewCustomFieldClick,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(24.dp))
BitwardenListHeaderText(
label = stringResource(id = R.string.ownership),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(8.dp))
BitwardenMultiSelectButton(
label = stringResource(id = R.string.who_owns_this_item),
options = state.availableOwners.toImmutableList(),
selectedOption = state.ownership,
onOptionSelected = secureNotesTypeHandlers.onOwnershipTextChange,
modifier = Modifier
.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.navigationBarsPadding())
}

View file

@ -4,11 +4,15 @@ import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.semantics.getOrNull
import androidx.compose.ui.test.SemanticsMatcher
import androidx.compose.ui.test.SemanticsNodeInteraction
import androidx.compose.ui.test.SemanticsNodeInteractionCollection
import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.test.hasScrollToNodeAction
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.onAllNodesWithContentDescription
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performScrollToNode
import org.junit.jupiter.api.assertThrows
@ -44,11 +48,44 @@ fun ComposeContentTestRule.onNodeWithTextAfterScroll(text: String): SemanticsNod
return onNodeWithText(text)
}
/**
* A helper used to scroll to and get the matching node in a scrollable list. This is intended to
* be used with lazy lists that would otherwise fail when calling [performScrollToNode].
*/
fun ComposeContentTestRule.onNodeWithContentDescriptionAfterScroll(
label: String,
): SemanticsNodeInteraction {
onNode(hasScrollToNodeAction()).performScrollToNode(hasContentDescription(label))
return onNodeWithContentDescription(label)
}
/**
* A helper used to scroll to and get a thr first matching node in a scrollable list. This is
* intended to be used with lazy lists that would otherwise fail when calling [performScrollToNode].
*/
fun ComposeContentTestRule.onFirstNodeWithTextAfterScroll(text: String): SemanticsNodeInteraction {
fun ComposeContentTestRule.onAllNodesWithTextAfterScroll(
text: String,
): SemanticsNodeInteractionCollection {
onNode(hasScrollToNodeAction()).performScrollToNode(hasText(text))
return onAllNodesWithText(text).onFirst()
return onAllNodesWithText(text)
}
/**
* A helper used to scroll to and get a thr first matching node in a scrollable list. This is
* intended to be used with lazy lists that would otherwise fail when calling [performScrollToNode].
*/
fun ComposeContentTestRule.onAllNodesWithContentDescriptionAfterScroll(
label: String,
): SemanticsNodeInteractionCollection {
onNode(hasScrollToNodeAction()).performScrollToNode(hasContentDescription(label))
return onAllNodesWithContentDescription(label)
}
/**
* A helper used to scroll to and get all matching nodes in a scrollable list. This is intended
* to be used with lazy lists that would otherwise fail when calling [performScrollToNode].
*/
fun ComposeContentTestRule.onFirstNodeWithTextAfterScroll(
text: String,
): SemanticsNodeInteraction =
onAllNodesWithTextAfterScroll(text).onFirst()

View file

@ -9,7 +9,6 @@ import androidx.compose.ui.test.click
import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.test.hasSetTextAction
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onLast
@ -22,6 +21,9 @@ import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.test.performTouchInput
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
import com.x8bit.bitwarden.ui.platform.base.util.asText
import com.x8bit.bitwarden.ui.util.onAllNodesWithTextAfterScroll
import com.x8bit.bitwarden.ui.util.onNodeWithContentDescriptionAfterScroll
import com.x8bit.bitwarden.ui.util.onNodeWithTextAfterScroll
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
@ -85,7 +87,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
// Opens the menu
composeTestRule
.onNodeWithContentDescription(label = "Type, Login")
.onNodeWithContentDescriptionAfterScroll(label = "Type, Login")
.performClick()
// Choose the option from the menu
@ -109,13 +111,13 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Type, Login")
.onNodeWithContentDescriptionAfterScroll(label = "Type, Login")
.assertIsDisplayed()
mutableStateFlow.update { it.copy(selectedType = VaultAddItemState.ItemType.Card) }
composeTestRule
.onNodeWithContentDescription(label = "Type, Card")
.onNodeWithContentDescriptionAfterScroll(label = "Type, Card")
.assertIsDisplayed()
}
@ -126,7 +128,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Name")
.onNodeWithTextAfterScroll(text = "Name")
.performTextInput(text = "TestName")
verify {
@ -143,7 +145,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Name")
.onNodeWithTextAfterScroll(text = "Name")
.assertTextContains("")
mutableStateFlow.update { currentState ->
@ -151,7 +153,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Name")
.onNodeWithTextAfterScroll(text = "Name")
.assertTextContains("NewName")
}
@ -162,7 +164,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Username")
.onNodeWithTextAfterScroll(text = "Username")
.performTextInput(text = "TestUsername")
verify {
@ -179,7 +181,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Username")
.onNodeWithTextAfterScroll(text = "Username")
.assertTextContains("")
mutableStateFlow.update { currentState ->
@ -187,7 +189,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Username")
.onNodeWithTextAfterScroll(text = "Username")
.assertTextContains("NewUsername")
}
@ -199,7 +201,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Generate username")
.onNodeWithContentDescriptionAfterScroll(label = "Generate username")
.performClick()
verify {
@ -217,10 +219,9 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Password")
.onNodeWithTextAfterScroll(text = "Password")
.onSiblings()
.onFirst()
.performScrollTo()
.performClick()
verify {
@ -236,10 +237,9 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Password")
.onNodeWithTextAfterScroll(text = "Password")
.onSiblings()
.onLast()
.performScrollTo()
.performClick()
verify {
@ -256,7 +256,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Password")
.onNodeWithTextAfterScroll(text = "Password")
.performTextInput(text = "TestPassword")
verify {
@ -273,7 +273,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Password")
.onNodeWithTextAfterScroll(text = "Password")
.assertTextContains("")
mutableStateFlow.update { currentState ->
@ -281,7 +281,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Password")
.onNodeWithTextAfterScroll(text = "Password")
.assertTextContains("•••••••••••")
}
@ -292,8 +292,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Set up TOTP")
.performScrollTo()
.onNodeWithTextAfterScroll(text = "Set up TOTP")
.performClick()
verify {
@ -310,8 +309,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText("URI")
.performScrollTo()
.onNodeWithTextAfterScroll("URI")
.performTextInput("TestURI")
verify {
@ -328,7 +326,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "URI")
.onNodeWithTextAfterScroll("URI")
.assertTextContains("")
mutableStateFlow.update { currentState ->
@ -336,7 +334,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "URI")
.onNodeWithTextAfterScroll(text = "URI")
.assertTextContains("NewURI")
}
@ -348,10 +346,9 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "URI")
.onNodeWithTextAfterScroll(text = "URI")
.onSiblings()
.filterToOne(hasContentDescription(value = "Options"))
.performScrollTo()
.performClick()
verify {
@ -368,8 +365,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "New URI")
.performScrollTo()
.onNodeWithTextAfterScroll(text = "New URI")
.performClick()
verify {
@ -387,8 +383,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
// Opens the menu
composeTestRule
.onNodeWithContentDescription(label = "Folder, No Folder")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Folder, No Folder")
.performClick()
// Choose the option from the menu
@ -412,8 +407,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Folder, No Folder")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Folder, No Folder")
.assertIsDisplayed()
mutableStateFlow.update { currentState ->
@ -421,8 +415,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Folder, Folder 2")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Folder, Folder 2")
.assertIsDisplayed()
}
@ -433,8 +426,8 @@ class VaultAddItemScreenTest : BaseComposeTest() {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
}
composeTestRule.onNodeWithText("Favorite")
.performScrollTo()
composeTestRule
.onNodeWithTextAfterScroll("Favorite")
.performClick()
verify {
@ -453,7 +446,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Favorite")
.onNodeWithTextAfterScroll("Favorite")
.assertIsOff()
mutableStateFlow.update { currentState ->
@ -461,7 +454,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Favorite")
.onNodeWithTextAfterScroll("Favorite")
.assertIsOn()
}
@ -473,8 +466,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText("Master password re-prompt")
.performScrollTo()
.onNodeWithTextAfterScroll("Master password re-prompt")
.performTouchInput {
click(position = Offset(x = 1f, y = center.y))
}
@ -496,7 +488,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText("Master password re-prompt")
.onNodeWithTextAfterScroll("Master password re-prompt")
.assertIsOff()
mutableStateFlow.update { currentState ->
@ -504,7 +496,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText("Master password re-prompt")
.onNodeWithTextAfterScroll("Master password re-prompt")
.assertIsOn()
}
@ -515,8 +507,8 @@ class VaultAddItemScreenTest : BaseComposeTest() {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
}
composeTestRule.onNodeWithContentDescription(label = "Master password re-prompt help")
.performScrollTo()
composeTestRule
.onNodeWithContentDescriptionAfterScroll(label = "Master password re-prompt help")
.performClick()
verify {
@ -533,8 +525,8 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNode(hasSetTextAction() and hasText("Notes"))
.performScrollTo()
.onAllNodesWithTextAfterScroll("Notes")
.filterToOne(hasSetTextAction())
.performTextInput("TestNotes")
verify {
@ -551,15 +543,17 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNode(hasSetTextAction() and hasText("Notes"))
.assertTextContains("")
.onAllNodesWithTextAfterScroll("Notes")
.filterToOne(hasSetTextAction())
.performTextInput("")
mutableStateFlow.update { currentState ->
updateLoginType(currentState) { copy(notes = "NewNote") }
}
composeTestRule
.onNode(hasSetTextAction() and hasText("Notes"))
.onAllNodesWithTextAfterScroll("Notes")
.filterToOne(hasSetTextAction())
.assertTextContains("NewNote")
}
@ -571,8 +565,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "New custom field")
.performScrollTo()
.onNodeWithTextAfterScroll(text = "New custom field")
.performClick()
verify {
@ -588,8 +581,9 @@ class VaultAddItemScreenTest : BaseComposeTest() {
// Opens the menu
composeTestRule
.onNodeWithContentDescription(label = "Who owns this item?, placeholder@email.com")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(
label = "Who owns this item?, placeholder@email.com",
)
.performClick()
// Choose the option from the menu
@ -613,8 +607,9 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Who owns this item?, placeholder@email.com")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(
label = "Who owns this item?, placeholder@email.com",
)
.assertIsDisplayed()
mutableStateFlow.update { currentState ->
@ -622,8 +617,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Who owns this item?, Owner 2")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Who owns this item?, Owner 2")
.assertIsDisplayed()
}
@ -637,7 +631,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Name")
.onNodeWithTextAfterScroll(text = "Name")
.performTextInput(text = "TestName")
verify {
@ -657,7 +651,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Name")
.onNodeWithTextAfterScroll(text = "Name")
.assertTextContains("")
mutableStateFlow.update { currentState ->
@ -665,7 +659,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "Name")
.onNodeWithTextAfterScroll(text = "Name")
.assertTextContains("NewName")
}
@ -680,8 +674,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
// Opens the menu
composeTestRule
.onNodeWithContentDescription(label = "Folder, No Folder")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Folder, No Folder")
.performClick()
// Choose the option from the menu
@ -709,8 +702,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Folder, No Folder")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Folder, No Folder")
.assertIsDisplayed()
mutableStateFlow.update { currentState ->
@ -718,8 +710,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Folder, Folder 2")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Folder, Folder 2")
.assertIsDisplayed()
}
@ -734,8 +725,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText("Favorite")
.performScrollTo()
.onNodeWithTextAfterScroll("Favorite")
.performClick()
verify {
@ -758,7 +748,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText("Favorite")
.onNodeWithTextAfterScroll("Favorite")
.assertIsOff()
mutableStateFlow.update { currentState ->
@ -766,7 +756,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText("Favorite")
.onNodeWithTextAfterScroll("Favorite")
.assertIsOn()
}
@ -781,8 +771,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText("Master password re-prompt")
.performScrollTo()
.onNodeWithTextAfterScroll("Master password re-prompt")
.performTouchInput {
click(position = Offset(x = 1f, y = center.y))
}
@ -807,7 +796,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText("Master password re-prompt")
.onNodeWithTextAfterScroll("Master password re-prompt")
.assertIsOff()
mutableStateFlow.update { currentState ->
@ -815,7 +804,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText("Master password re-prompt")
.onNodeWithTextAfterScroll("Master password re-prompt")
.assertIsOn()
}
@ -830,8 +819,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Master password re-prompt help")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Master password re-prompt help")
.performClick()
verify {
@ -851,8 +839,8 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNode(hasSetTextAction() and hasText("Notes"))
.performScrollTo()
.onAllNodesWithTextAfterScroll("Notes")
.filterToOne(hasSetTextAction())
.performTextInput("TestNotes")
verify {
@ -873,7 +861,8 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNode(hasSetTextAction() and hasText("Notes"))
.onAllNodesWithTextAfterScroll("Notes")
.filterToOne(hasSetTextAction())
.assertTextContains("")
mutableStateFlow.update { currentState ->
@ -881,7 +870,8 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNode(hasSetTextAction() and hasText("Notes"))
.onAllNodesWithTextAfterScroll("Notes")
.filterToOne(hasSetTextAction())
.assertTextContains("NewNote")
}
@ -896,8 +886,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithText(text = "New custom field")
.performScrollTo()
.onNodeWithTextAfterScroll(text = "New custom field")
.performClick()
verify {
@ -919,8 +908,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
// Opens the menu
composeTestRule
.onNodeWithContentDescription(label = "Who owns this item?, placeholder@email.com")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Who owns this item?, placeholder@email.com")
.performClick()
// Choose the option from the menu
@ -948,8 +936,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Who owns this item?, placeholder@email.com")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Who owns this item?, placeholder@email.com")
.assertIsDisplayed()
mutableStateFlow.update { currentState ->
@ -957,8 +944,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
composeTestRule
.onNodeWithContentDescription(label = "Who owns this item?, Owner 2")
.performScrollTo()
.onNodeWithContentDescriptionAfterScroll(label = "Who owns this item?, Owner 2")
.assertIsDisplayed()
}