mirror of
https://github.com/bitwarden/android.git
synced 2024-11-21 17:05:44 +03:00
[PM-13831] Add copy button identity and note fields (#4302)
This commit is contained in:
parent
531b003347
commit
d418444dc0
11 changed files with 798 additions and 58 deletions
|
@ -16,6 +16,7 @@ import com.x8bit.bitwarden.R
|
|||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenPasswordFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCardItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
|
||||
|
@ -173,14 +174,22 @@ fun VaultItemCardContent(
|
|||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
BitwardenTextFieldWithActions(
|
||||
label = stringResource(id = R.string.notes),
|
||||
value = notes,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_notes),
|
||||
onClick = vaultCommonItemTypeHandlers.onCopyNotesClick,
|
||||
modifier = Modifier.testTag(tag = "CipherNotesCopyButton"),
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "CipherNotesLabel",
|
||||
modifier = Modifier
|
||||
.testTag("CipherNotesLabel")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
|
|
@ -13,19 +13,23 @@ import androidx.compose.ui.platform.testTag
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultIdentityItemTypeHandlers
|
||||
|
||||
/**
|
||||
* The top level content UI state for the [VaultItemScreen] when viewing a Identity cipher.
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
@Suppress("LongMethod", "MaxLineLength")
|
||||
@Composable
|
||||
fun VaultItemIdentityContent(
|
||||
identityState: VaultItemState.ViewState.Content.ItemType.Identity,
|
||||
commonState: VaultItemState.ViewState.Content.Common,
|
||||
vaultCommonItemTypeHandlers: VaultCommonItemTypeHandlers,
|
||||
vaultIdentityItemTypeHandlers: VaultIdentityItemTypeHandlers,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(modifier = modifier) {
|
||||
|
@ -54,14 +58,14 @@ fun VaultItemIdentityContent(
|
|||
identityState.identityName?.let { identityName ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.identity_name),
|
||||
value = identityName,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
copyContentDescription = stringResource(id = R.string.copy_identity_name),
|
||||
textFieldTestTag = "IdentityNameEntry",
|
||||
copyActionTestTag = "IdentityCopyNameButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyIdentityNameClick,
|
||||
modifier = Modifier
|
||||
.testTag("IdentityNameEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
@ -70,14 +74,14 @@ fun VaultItemIdentityContent(
|
|||
identityState.username?.let { username ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.username),
|
||||
value = username,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
copyContentDescription = stringResource(id = R.string.copy_username),
|
||||
textFieldTestTag = "IdentityUsernameEntry",
|
||||
copyActionTestTag = "IdentityCopyUsernameButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyUsernameClick,
|
||||
modifier = Modifier
|
||||
.testTag("IdentityUsernameEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
@ -86,14 +90,14 @@ fun VaultItemIdentityContent(
|
|||
identityState.company?.let { company ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.company),
|
||||
value = company,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
copyContentDescription = stringResource(id = R.string.copy_company),
|
||||
textFieldTestTag = "IdentityCompanyEntry",
|
||||
copyActionTestTag = "IdentityCopyCompanyButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyCompanyClick,
|
||||
modifier = Modifier
|
||||
.testTag("IdentityCompanyEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
@ -102,14 +106,14 @@ fun VaultItemIdentityContent(
|
|||
identityState.ssn?.let { ssn ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.ssn),
|
||||
value = ssn,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
copyContentDescription = stringResource(id = R.string.copy_ssn),
|
||||
textFieldTestTag = "IdentitySsnEntry",
|
||||
copyActionTestTag = "IdentityCopySsnButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopySsnClick,
|
||||
modifier = Modifier
|
||||
.testTag("IdentitySsnEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
@ -118,14 +122,14 @@ fun VaultItemIdentityContent(
|
|||
identityState.passportNumber?.let { passportNumber ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.passport_number),
|
||||
value = passportNumber,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
copyContentDescription = stringResource(id = R.string.copy_passport_number),
|
||||
textFieldTestTag = "IdentityPassportNumberEntry",
|
||||
copyActionTestTag = "IdentityCopyPassportNumberButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyPassportNumberClick,
|
||||
modifier = Modifier
|
||||
.testTag("IdentityPassportNumberEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
@ -134,14 +138,14 @@ fun VaultItemIdentityContent(
|
|||
identityState.licenseNumber?.let { licenseNumber ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.license_number),
|
||||
value = licenseNumber,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
copyContentDescription = stringResource(id = R.string.copy_license_number),
|
||||
textFieldTestTag = "IdentityLicenseNumberEntry",
|
||||
copyActionTestTag = "IdentityCopyLicenseNumberButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyLicenseNumberClick,
|
||||
modifier = Modifier
|
||||
.testTag("IdentityLicenseNumberEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
@ -150,14 +154,14 @@ fun VaultItemIdentityContent(
|
|||
identityState.email?.let { email ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.email),
|
||||
value = email,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
copyContentDescription = stringResource(id = R.string.copy_email),
|
||||
textFieldTestTag = "IdentityEmailEntry",
|
||||
copyActionTestTag = "IdentityCopyEmailButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyEmailClick,
|
||||
modifier = Modifier
|
||||
.testTag("IdentityEmailEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
@ -166,14 +170,14 @@ fun VaultItemIdentityContent(
|
|||
identityState.phone?.let { phone ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.phone),
|
||||
value = phone,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
copyContentDescription = stringResource(id = R.string.copy_phone),
|
||||
textFieldTestTag = "IdentityPhoneEntry",
|
||||
copyActionTestTag = "IdentityCopyPhoneButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyPhoneClick,
|
||||
modifier = Modifier
|
||||
.testTag("IdentityPhoneEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
@ -182,20 +186,19 @@ fun VaultItemIdentityContent(
|
|||
identityState.address?.let { address ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.address),
|
||||
value = address,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
copyContentDescription = stringResource(id = R.string.copy_address),
|
||||
textFieldTestTag = "IdentityAddressEntry",
|
||||
copyActionTestTag = "IdentityCopyAddressButton",
|
||||
onCopyClick = vaultIdentityItemTypeHandlers.onCopyAddressClick,
|
||||
modifier = Modifier
|
||||
.testTag("IdentityAddressEntry")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
commonState.notes?.let { notes ->
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
@ -206,14 +209,14 @@ fun VaultItemIdentityContent(
|
|||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
IdentityCopyField(
|
||||
label = stringResource(id = R.string.notes),
|
||||
value = notes,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
copyContentDescription = stringResource(id = R.string.copy_notes),
|
||||
textFieldTestTag = "CipherNotesLabel",
|
||||
copyActionTestTag = "CipherNotesCopyButton",
|
||||
onCopyClick = vaultCommonItemTypeHandlers.onCopyNotesClick,
|
||||
modifier = Modifier
|
||||
.testTag("CipherNotesLabel")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
@ -284,3 +287,32 @@ fun VaultItemIdentityContent(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun IdentityCopyField(
|
||||
label: String,
|
||||
value: String,
|
||||
copyContentDescription: String,
|
||||
textFieldTestTag: String,
|
||||
copyActionTestTag: String,
|
||||
onCopyClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BitwardenTextFieldWithActions(
|
||||
label = label,
|
||||
value = value,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = copyContentDescription,
|
||||
onClick = onCopyClick,
|
||||
modifier = Modifier.testTag(tag = copyActionTestTag),
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
textFieldTestTag = textFieldTestTag,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -158,8 +158,8 @@ fun VaultItemLoginContent(
|
|||
Spacer(modifier = Modifier.height(8.dp))
|
||||
NotesField(
|
||||
notes = notes,
|
||||
onCopyAction = vaultCommonItemTypeHandlers.onCopyNotesClick,
|
||||
modifier = Modifier
|
||||
.testTag("CipherNotesLabel")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
@ -273,14 +273,24 @@ private fun Fido2CredentialField(
|
|||
@Composable
|
||||
private fun NotesField(
|
||||
notes: String,
|
||||
onCopyAction: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BitwardenTextField(
|
||||
BitwardenTextFieldWithActions(
|
||||
label = stringResource(id = R.string.notes),
|
||||
value = notes,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_notes),
|
||||
onClick = onCopyAction,
|
||||
modifier = Modifier.testTag(tag = "CipherNotesCopyButton"),
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "CipherNotesLabel",
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
|||
import com.x8bit.bitwarden.ui.platform.util.persistentListOfNotNull
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCardItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultIdentityItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultLoginItemTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultSshKeyItemTypeHandlers
|
||||
|
||||
|
@ -272,6 +273,9 @@ fun VaultItemScreen(
|
|||
vaultSshKeyItemTypeHandlers = remember(viewModel) {
|
||||
VaultSshKeyItemTypeHandlers.create(viewModel = viewModel)
|
||||
},
|
||||
vaultIdentityItemTypeHandlers = remember(viewModel) {
|
||||
VaultIdentityItemTypeHandlers.create(viewModel = viewModel)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -350,6 +354,7 @@ private fun VaultItemContent(
|
|||
vaultLoginItemTypeHandlers: VaultLoginItemTypeHandlers,
|
||||
vaultCardItemTypeHandlers: VaultCardItemTypeHandlers,
|
||||
vaultSshKeyItemTypeHandlers: VaultSshKeyItemTypeHandlers,
|
||||
vaultIdentityItemTypeHandlers: VaultIdentityItemTypeHandlers,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
when (viewState) {
|
||||
|
@ -386,6 +391,7 @@ private fun VaultItemContent(
|
|||
commonState = viewState.common,
|
||||
identityState = viewState.type,
|
||||
vaultCommonItemTypeHandlers = vaultCommonItemTypeHandlers,
|
||||
vaultIdentityItemTypeHandlers = vaultIdentityItemTypeHandlers,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,9 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.components.button.BitwardenTonalIconButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextField
|
||||
import com.x8bit.bitwarden.ui.platform.components.field.BitwardenTextFieldWithActions
|
||||
import com.x8bit.bitwarden.ui.platform.components.header.BitwardenListHeaderText
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.handlers.VaultCommonItemTypeHandlers
|
||||
|
@ -66,14 +68,22 @@ fun VaultItemSecureNoteContent(
|
|||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
BitwardenTextField(
|
||||
BitwardenTextFieldWithActions(
|
||||
label = stringResource(id = R.string.notes),
|
||||
value = notes,
|
||||
onValueChange = { },
|
||||
readOnly = true,
|
||||
singleLine = false,
|
||||
actions = {
|
||||
BitwardenTonalIconButton(
|
||||
vectorIconRes = R.drawable.ic_copy,
|
||||
contentDescription = stringResource(id = R.string.copy_notes),
|
||||
onClick = vaultCommonItemTypeHandlers.onCopyNotesClick,
|
||||
modifier = Modifier.testTag(tag = "CipherNotesCopyButton"),
|
||||
)
|
||||
},
|
||||
textFieldTestTag = "CipherNotesLabel",
|
||||
modifier = Modifier
|
||||
.testTag("CipherNotesLabel")
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp),
|
||||
)
|
||||
|
|
|
@ -133,6 +133,7 @@ class VaultItemViewModel @Inject constructor(
|
|||
is VaultItemAction.ItemType.Login -> handleLoginTypeActions(action)
|
||||
is VaultItemAction.ItemType.Card -> handleCardTypeActions(action)
|
||||
is VaultItemAction.ItemType.SshKey -> handleSshKeyTypeActions(action)
|
||||
is VaultItemAction.ItemType.Identity -> handleIdentityTypeActions(action)
|
||||
is VaultItemAction.Common -> handleCommonActions(action)
|
||||
is VaultItemAction.Internal -> handleInternalAction(action)
|
||||
}
|
||||
|
@ -184,6 +185,7 @@ class VaultItemViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is VaultItemAction.Common.RestoreVaultItemClick -> handleRestoreItemClicked()
|
||||
is VaultItemAction.Common.CopyNotesClick -> handleCopyNotesClick()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -508,6 +510,13 @@ class VaultItemViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleCopyNotesClick() {
|
||||
onContent { content ->
|
||||
val notes = content.common.notes.orEmpty()
|
||||
clipboardManager.setText(text = notes)
|
||||
}
|
||||
}
|
||||
|
||||
//endregion Common Handlers
|
||||
|
||||
//region Login Type Handlers
|
||||
|
@ -812,6 +821,99 @@ class VaultItemViewModel @Inject constructor(
|
|||
|
||||
//endregion SSH Key Type Handlers
|
||||
|
||||
//region Identity Type Handlers
|
||||
|
||||
private fun handleIdentityTypeActions(action: VaultItemAction.ItemType.Identity) {
|
||||
when (action) {
|
||||
VaultItemAction.ItemType.Identity.CopyIdentityNameClick -> {
|
||||
handleCopyIdentityNameClick()
|
||||
}
|
||||
|
||||
VaultItemAction.ItemType.Identity.CopyUsernameClick -> {
|
||||
handleCopyIdentityUsernameClick()
|
||||
}
|
||||
|
||||
VaultItemAction.ItemType.Identity.CopyCompanyClick -> handleCopyCompanyClick()
|
||||
VaultItemAction.ItemType.Identity.CopySsnClick -> handleCopySsnClick()
|
||||
VaultItemAction.ItemType.Identity.CopyPassportNumberClick -> {
|
||||
handleCopyPassportNumberClick()
|
||||
}
|
||||
|
||||
VaultItemAction.ItemType.Identity.CopyLicenseNumberClick -> {
|
||||
handleCopyLicenseNumberClick()
|
||||
}
|
||||
|
||||
VaultItemAction.ItemType.Identity.CopyEmailClick -> handleCopyEmailClick()
|
||||
VaultItemAction.ItemType.Identity.CopyPhoneClick -> handleCopyPhoneClick()
|
||||
VaultItemAction.ItemType.Identity.CopyAddressClick -> handleCopyAddressClick()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyIdentityNameClick() {
|
||||
onIdentityContent { _, identity ->
|
||||
val identityName = identity.identityName.orEmpty()
|
||||
clipboardManager.setText(text = identityName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyIdentityUsernameClick() {
|
||||
onIdentityContent { _, identity ->
|
||||
val username = identity.username.orEmpty()
|
||||
clipboardManager.setText(text = username)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyCompanyClick() {
|
||||
onIdentityContent { _, identity ->
|
||||
val company = identity.company.orEmpty()
|
||||
clipboardManager.setText(text = company)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopySsnClick() {
|
||||
onIdentityContent { _, identity ->
|
||||
val ssn = identity.ssn.orEmpty()
|
||||
clipboardManager.setText(text = ssn)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyPassportNumberClick() {
|
||||
onIdentityContent { _, identity ->
|
||||
val passportNumber = identity.passportNumber.orEmpty()
|
||||
clipboardManager.setText(text = passportNumber)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyLicenseNumberClick() {
|
||||
onIdentityContent { _, identity ->
|
||||
val licenseNumber = identity.licenseNumber.orEmpty()
|
||||
clipboardManager.setText(text = licenseNumber)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyEmailClick() {
|
||||
onIdentityContent { _, identity ->
|
||||
val email = identity.email.orEmpty()
|
||||
clipboardManager.setText(text = email)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyPhoneClick() {
|
||||
onIdentityContent { _, identity ->
|
||||
val phone = identity.phone.orEmpty()
|
||||
clipboardManager.setText(text = phone)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCopyAddressClick() {
|
||||
onIdentityContent { _, identity ->
|
||||
val address = identity.address.orEmpty()
|
||||
clipboardManager.setText(text = address)
|
||||
}
|
||||
}
|
||||
|
||||
//endregion Identity Type Handlers
|
||||
|
||||
//region Internal Type Handlers
|
||||
|
||||
private fun handleInternalAction(action: VaultItemAction.Internal) {
|
||||
|
@ -1133,6 +1235,21 @@ class VaultItemViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun onIdentityContent(
|
||||
crossinline block: (
|
||||
VaultItemState.ViewState.Content,
|
||||
VaultItemState.ViewState.Content.ItemType.Identity,
|
||||
) -> Unit,
|
||||
) {
|
||||
state.viewState.asContentOrNull()
|
||||
?.let { content ->
|
||||
(content.type as? VaultItemState.ViewState.Content.ItemType.Identity)
|
||||
?.let { identityContent ->
|
||||
block(content, identityContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1724,6 +1841,11 @@ sealed class VaultItemAction {
|
|||
* The user confirmed cloning a cipher without its FIDO 2 credentials.
|
||||
*/
|
||||
data object ConfirmCloneWithoutFido2CredentialClick : Common()
|
||||
|
||||
/**
|
||||
* The user has clicked the copy button for notes text field.
|
||||
*/
|
||||
data object CopyNotesClick : Common()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1827,6 +1949,56 @@ sealed class VaultItemAction {
|
|||
*/
|
||||
data object CopyFingerprintClick : SshKey()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents actions specific to the Identity type.
|
||||
*/
|
||||
sealed class Identity : VaultItemAction() {
|
||||
/**
|
||||
* The user has clicked the copy button for the identity name.
|
||||
*/
|
||||
data object CopyIdentityNameClick : Identity()
|
||||
|
||||
/**
|
||||
* The user has clicked the copy button for the username.
|
||||
*/
|
||||
data object CopyUsernameClick : Identity()
|
||||
|
||||
/**
|
||||
* The user has clicked the copy button for the company.
|
||||
*/
|
||||
data object CopyCompanyClick : Identity()
|
||||
|
||||
/**
|
||||
* The user has clicked the copy button for the SSN.
|
||||
*/
|
||||
data object CopySsnClick : Identity()
|
||||
|
||||
/**
|
||||
* The user has clicked the copy button for the passport number.
|
||||
*/
|
||||
data object CopyPassportNumberClick : Identity()
|
||||
|
||||
/**
|
||||
* The user has clicked the copy button for the license number.
|
||||
*/
|
||||
data object CopyLicenseNumberClick : Identity()
|
||||
|
||||
/**
|
||||
* The user has clicked the copy button for the email.
|
||||
*/
|
||||
data object CopyEmailClick : Identity()
|
||||
|
||||
/**
|
||||
* The user has clicked the copy button for the phone number.
|
||||
*/
|
||||
data object CopyPhoneClick : Identity()
|
||||
|
||||
/**
|
||||
* The user has clicked the copy button for the address.
|
||||
*/
|
||||
data object CopyAddressClick : Identity()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,7 @@ data class VaultCommonItemTypeHandlers(
|
|||
Boolean,
|
||||
) -> Unit,
|
||||
val onAttachmentDownloadClick: (VaultItemState.ViewState.Content.Common.AttachmentItem) -> Unit,
|
||||
val onCopyNotesClick: () -> Unit,
|
||||
) {
|
||||
@Suppress("UndocumentedPublicClass")
|
||||
companion object {
|
||||
|
@ -52,6 +53,9 @@ data class VaultCommonItemTypeHandlers(
|
|||
onAttachmentDownloadClick = {
|
||||
viewModel.trySendAction(VaultItemAction.Common.AttachmentDownloadClick(it))
|
||||
},
|
||||
onCopyNotesClick = {
|
||||
viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.item.handlers
|
||||
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.VaultItemAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.VaultItemViewModel
|
||||
|
||||
/**
|
||||
* A collection of handler functions for managing actions within the context of viewing identity
|
||||
* items in a vault.
|
||||
*/
|
||||
data class VaultIdentityItemTypeHandlers(
|
||||
val onCopyIdentityNameClick: () -> Unit,
|
||||
val onCopyUsernameClick: () -> Unit,
|
||||
val onCopyCompanyClick: () -> Unit,
|
||||
val onCopySsnClick: () -> Unit,
|
||||
val onCopyPassportNumberClick: () -> Unit,
|
||||
val onCopyLicenseNumberClick: () -> Unit,
|
||||
val onCopyEmailClick: () -> Unit,
|
||||
val onCopyPhoneClick: () -> Unit,
|
||||
val onCopyAddressClick: () -> Unit,
|
||||
) {
|
||||
@Suppress("UndocumentedPublicClass", "MaxLineLength")
|
||||
companion object {
|
||||
/**
|
||||
* Creates the [VaultIdentityItemTypeHandlers] using the [viewModel] to send desired actions.
|
||||
*/
|
||||
fun create(
|
||||
viewModel: VaultItemViewModel,
|
||||
): VaultIdentityItemTypeHandlers =
|
||||
VaultIdentityItemTypeHandlers(
|
||||
onCopyIdentityNameClick = {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyIdentityNameClick)
|
||||
},
|
||||
onCopyUsernameClick = {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyUsernameClick)
|
||||
},
|
||||
onCopyCompanyClick = {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyCompanyClick)
|
||||
},
|
||||
onCopySsnClick = {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopySsnClick)
|
||||
},
|
||||
onCopyPassportNumberClick = {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPassportNumberClick)
|
||||
},
|
||||
onCopyLicenseNumberClick = {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyLicenseNumberClick)
|
||||
},
|
||||
onCopyEmailClick = {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyEmailClick)
|
||||
},
|
||||
onCopyPhoneClick = {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPhoneClick)
|
||||
},
|
||||
onCopyAddressClick = {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyAddressClick)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1087,4 +1087,12 @@ Do you want to switch to this account?</string>
|
|||
<string name="skip_for_now">Skip for now</string>
|
||||
<string name="done_text">Done</string>
|
||||
<string name="page_number_x_of_y">%1$s of %2$s</string>
|
||||
<string name="copy_identity_name">Copy identity name</string>
|
||||
<string name="copy_company">Copy company</string>
|
||||
<string name="copy_ssn">Copy social security number</string>
|
||||
<string name="copy_passport_number">Copy passport number</string>
|
||||
<string name="copy_license_number">Copy license number</string>
|
||||
<string name="copy_email">Copy email</string>
|
||||
<string name="copy_phone">Copy phone number</string>
|
||||
<string name="copy_address">Copy address</string>
|
||||
</resources>
|
||||
|
|
|
@ -19,6 +19,7 @@ import androidx.compose.ui.test.onAllNodesWithText
|
|||
import androidx.compose.ui.test.onChildren
|
||||
import androidx.compose.ui.test.onLast
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.onSiblings
|
||||
import androidx.compose.ui.test.performClick
|
||||
|
@ -1257,6 +1258,114 @@ class VaultItemScreenTest : BaseComposeTest() {
|
|||
.filterToOne(hasAnyAncestor(isPopup()))
|
||||
.assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on login copy notes field click should send CopyNotesClick`() {
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
currentState.copy(
|
||||
viewState = DEFAULT_LOGIN_VIEW_STATE,
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithTextAfterScroll("Lots of notes")
|
||||
composeTestRule
|
||||
.onNodeWithTag("CipherNotesCopyButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on identity copy notes field click should send CopyNotesClick`() {
|
||||
// Adding a custom field so that we can scroll to it
|
||||
// So we can see the Copy notes button but not have it covered by the FAB
|
||||
val textField = VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
)
|
||||
|
||||
EMPTY_VIEW_STATES
|
||||
.forEach { typeState ->
|
||||
mutableStateFlow.update { currentState ->
|
||||
currentState.copy(
|
||||
viewState = typeState.copy(
|
||||
type = DEFAULT_IDENTITY,
|
||||
common = EMPTY_COMMON.copy(
|
||||
notes = "this is a note",
|
||||
customFields = listOf(textField),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithTextAfterScroll(textField.name)
|
||||
composeTestRule
|
||||
.onNodeWithTag("CipherNotesCopyButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on card copy notes field click should send CopyNotesClick`() {
|
||||
// Adding a custom field so that we can scroll to it
|
||||
// So we can see the Copy notes button but not have it covered by the FAB
|
||||
val textField = VaultItemState.ViewState.Content.Common.Custom.TextField(
|
||||
name = "text",
|
||||
value = "value",
|
||||
isCopyable = true,
|
||||
)
|
||||
|
||||
EMPTY_VIEW_STATES
|
||||
.forEach { typeState ->
|
||||
mutableStateFlow.update { currentState ->
|
||||
currentState.copy(
|
||||
viewState = typeState.copy(
|
||||
type = DEFAULT_IDENTITY,
|
||||
common = EMPTY_COMMON.copy(
|
||||
notes = "this is a note",
|
||||
customFields = listOf(textField),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithTextAfterScroll(textField.name)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("CipherNotesCopyButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on secure note copy notes field click should send CopyNotesClick`() {
|
||||
|
||||
mutableStateFlow.update { currentState ->
|
||||
currentState.copy(
|
||||
viewState = DEFAULT_SECURE_NOTE_VIEW_STATE,
|
||||
)
|
||||
}
|
||||
composeTestRule.onNodeWithTextAfterScroll("Lots of notes")
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("CipherNotesCopyButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick)
|
||||
}
|
||||
}
|
||||
//endregion common
|
||||
|
||||
//region login
|
||||
|
@ -1927,6 +2036,144 @@ class VaultItemScreenTest : BaseComposeTest() {
|
|||
|
||||
composeTestRule.assertScrollableNodeDoesNotExist(identityName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in identity state, on copy identity name field click should send CopyIdentityNameClick`() {
|
||||
|
||||
val identityName = "the identity name"
|
||||
mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) }
|
||||
composeTestRule.onNodeWithTextAfterScroll(identityName)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("IdentityCopyNameButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyIdentityNameClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in identity state, on copy username field click should send CopyUsernameClick`() {
|
||||
val username = "the username"
|
||||
mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) }
|
||||
composeTestRule.onNodeWithTextAfterScroll(username)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("IdentityCopyUsernameButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyUsernameClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in identity state, on copy company field click should send CopyCompanyClick`() {
|
||||
mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) }
|
||||
|
||||
// Scroll to ssn so we can see the Copy company button but not have it covered by the FAB
|
||||
composeTestRule.onNodeWithTextAfterScroll("the SSN")
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("IdentityCopyCompanyButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyCompanyClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in identity state, on copy SSN field click should send CopySsnClick`() {
|
||||
val ssn = "the SSN"
|
||||
mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) }
|
||||
composeTestRule.onNodeWithTextAfterScroll(ssn)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("IdentityCopySsnButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopySsnClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in identity state, on copy passport number field click should send CopyPassportNumberClick`() {
|
||||
val passportNumber = "the passport number"
|
||||
mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) }
|
||||
composeTestRule.onNodeWithTextAfterScroll(passportNumber)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("IdentityCopyPassportNumberButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPassportNumberClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in identity state, on copy license number field click should send CopyLicenseNumberClick`() {
|
||||
val licenseNumber = "the license number"
|
||||
mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) }
|
||||
composeTestRule.onNodeWithTextAfterScroll(licenseNumber)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("IdentityCopyLicenseNumberButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyLicenseNumberClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in identity state, on copy email field click should send CopyEmailClick`() {
|
||||
mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) }
|
||||
composeTestRule.onFirstNodeWithTextAfterScroll("the address")
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithContentDescriptionAfterScroll("Copy email")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyEmailClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in identity state, on copy phone field click should send CopyPhoneClick`() {
|
||||
val phone = "the phone number"
|
||||
mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) }
|
||||
composeTestRule.onNodeWithTextAfterScroll(phone)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("IdentityCopyPhoneButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPhoneClick)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in identity state, on copy address field click should send CopyAddressClick`() {
|
||||
val address = "the address"
|
||||
mutableStateFlow.update { it.copy(viewState = DEFAULT_IDENTITY_VIEW_STATE) }
|
||||
composeTestRule.onNodeWithTextAfterScroll(address)
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithTag("IdentityCopyAddressButton")
|
||||
.performClick()
|
||||
|
||||
verify {
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyAddressClick)
|
||||
}
|
||||
}
|
||||
//endregion identity
|
||||
|
||||
//region card
|
||||
|
|
|
@ -1670,6 +1670,33 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
|||
|
||||
coVerify { mockFileManager.delete(file) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyNotesFieldClick should call setText on ClipboardManager`() {
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canAssignToCollections = true,
|
||||
)
|
||||
} returns DEFAULT_VIEW_STATE
|
||||
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
|
||||
mutableAuthCodeItemFlow.value = DataState.Loaded(data = null)
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
|
||||
|
||||
val notes = "Lots of notes"
|
||||
every { clipboardManager.setText(text = notes) } just runs
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.Common.CopyNotesClick)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(text = notes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
@ -2527,6 +2554,143 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class IdentityActions {
|
||||
private lateinit var viewModel: VaultItemViewModel
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
viewModel = createViewModel(
|
||||
state = DEFAULT_STATE.copy(
|
||||
viewState = IDENTITY_VIEW_STATE,
|
||||
),
|
||||
)
|
||||
every {
|
||||
mockCipherView.toViewState(
|
||||
previousState = null,
|
||||
isPremiumUser = true,
|
||||
hasMasterPassword = true,
|
||||
totpCodeItemData = null,
|
||||
canDelete = true,
|
||||
canAssignToCollections = true,
|
||||
)
|
||||
} returns IDENTITY_VIEW_STATE
|
||||
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
|
||||
mutableAuthCodeItemFlow.value = DataState.Loaded(data = null)
|
||||
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyIdentityNameClick should copy fingerprint to clipboard`() =
|
||||
runTest {
|
||||
val username = "the username"
|
||||
every { clipboardManager.setText(text = username) } just runs
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyUsernameClick)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(text = username)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyUsernameClick should copy fingerprint to clipboard`() =
|
||||
runTest {
|
||||
val identityName = "the identity name"
|
||||
every { clipboardManager.setText(text = identityName) } just runs
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyIdentityNameClick)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(text = identityName)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyCompanyClick should copy company to clipboard`() = runTest {
|
||||
val company = "the company name"
|
||||
every { clipboardManager.setText(text = company) } just runs
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyCompanyClick)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(text = company)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopySsnClick should copy SSN to clipboard`() = runTest {
|
||||
val ssn = "the SSN"
|
||||
every { clipboardManager.setText(text = ssn) } just runs
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopySsnClick)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(text = ssn)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyPassportNumberClick should copy passport number to clipboard`() = runTest {
|
||||
val passportNumber = "the passport number"
|
||||
every { clipboardManager.setText(text = passportNumber) } just runs
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPassportNumberClick)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(text = passportNumber)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyLicenseNumberClick should copy license number to clipboard`() = runTest {
|
||||
val licenseNumber = "the license number"
|
||||
every { clipboardManager.setText(text = licenseNumber) } just runs
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyLicenseNumberClick)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(text = licenseNumber)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyEmailClick should copy email to clipboard`() = runTest {
|
||||
val email = "the email address"
|
||||
every { clipboardManager.setText(text = email) } just runs
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyEmailClick)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(text = email)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyPhoneClick should copy phone to clipboard`() = runTest {
|
||||
val phone = "the phone number"
|
||||
every { clipboardManager.setText(text = phone) } just runs
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyPhoneClick)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(text = phone)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on CopyAddressClick should copy address to clipboard`() = runTest {
|
||||
val address = "the address"
|
||||
every { clipboardManager.setText(text = address) } just runs
|
||||
|
||||
viewModel.trySendAction(VaultItemAction.ItemType.Identity.CopyAddressClick)
|
||||
|
||||
verify(exactly = 1) {
|
||||
clipboardManager.setText(text = address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class VaultItemFlow {
|
||||
@BeforeEach
|
||||
|
@ -2844,6 +3008,19 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
|||
showPrivateKey = false,
|
||||
)
|
||||
|
||||
private val DEFAULT_IDENTITY_TYPE: VaultItemState.ViewState.Content.ItemType.Identity =
|
||||
VaultItemState.ViewState.Content.ItemType.Identity(
|
||||
username = "the username",
|
||||
identityName = "the identity name",
|
||||
company = "the company name",
|
||||
ssn = "the SSN",
|
||||
passportNumber = "the passport number",
|
||||
licenseNumber = "the license number",
|
||||
email = "the email address",
|
||||
phone = "the phone number",
|
||||
address = "the address",
|
||||
)
|
||||
|
||||
private val DEFAULT_COMMON: VaultItemState.ViewState.Content.Common =
|
||||
VaultItemState.ViewState.Content.Common(
|
||||
name = "login cipher",
|
||||
|
@ -2908,5 +3085,11 @@ class VaultItemViewModelTest : BaseViewModelTest() {
|
|||
common = DEFAULT_COMMON,
|
||||
type = DEFAULT_SSH_KEY_TYPE,
|
||||
)
|
||||
|
||||
private val IDENTITY_VIEW_STATE: VaultItemState.ViewState.Content =
|
||||
VaultItemState.ViewState.Content(
|
||||
common = DEFAULT_COMMON,
|
||||
type = DEFAULT_IDENTITY_TYPE,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue