Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ data class AutofillSelectionData(
enum class Type {
CARD,
LOGIN,
IDENTITY,
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ fun FilledData.buildVaultItemDataset(
type = when (this.originalPartition) {
is AutofillPartition.Card -> AutofillSelectionData.Type.CARD
is AutofillPartition.Login -> AutofillSelectionData.Type.LOGIN
// AutofillSelectionData.Type has no IDENTITY value yet (deliberately deferred -- it
// cascades into the vault item listing/add-edit UI flows). This is a no-op placeholder
// today since nothing yet produces an Identity-only partition.
is AutofillPartition.Identity -> AutofillSelectionData.Type.LOGIN
is AutofillPartition.Identity -> AutofillSelectionData.Type.IDENTITY
},
uri = this.uri,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ fun AutofillSelectionData.Type.toVaultItemListingType(): VaultItemListingType =
when (this) {
AutofillSelectionData.Type.CARD -> VaultItemListingType.Card
AutofillSelectionData.Type.LOGIN -> VaultItemListingType.Login
AutofillSelectionData.Type.IDENTITY -> VaultItemListingType.Identity
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ fun AutofillSelectionData.toDefaultAddTypeContent(
),
)
}

AutofillSelectionData.Type.IDENTITY -> {
VaultAddEditState.ViewState.Content.ItemType.Identity()
}
}
return VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2867,6 +2867,20 @@ class VaultItemListingViewModel @Inject constructor(
)
}
}

AutofillSelectionData.Type.IDENTITY -> {
this.map { vaultData ->
vaultData.copy(
decryptCipherListResult = vaultData.decryptCipherListResult.copy(
successes = vaultData
.decryptCipherListResult
.successes
.filter { it.type is CipherListViewType.Identity },
Comment thread
david-livefront marked this conversation as resolved.
failures = emptyList(),
),
)
}
}
}
}

Expand Down Expand Up @@ -3039,19 +3053,23 @@ data class VaultItemListingState(
val appBarTitle: Text
get() = autofillSelectionData
?.let { data ->
data.uri
?.toHostOrPathOrNull()
?.let {
when (data.type) {
AutofillSelectionData.Type.CARD -> {
BitwardenString.select_a_card_for_x.asText(it)
}
when (data.type) {
AutofillSelectionData.Type.IDENTITY -> {
BitwardenString.choose_an_identity.asText()
}

AutofillSelectionData.Type.LOGIN -> {
BitwardenString.items_for_uri.asText(it)
}
}
AutofillSelectionData.Type.CARD -> {
data.uri
?.toHostOrPathOrNull()
?.let { BitwardenString.select_a_card_for_x.asText(it) }
}

AutofillSelectionData.Type.LOGIN -> {
data.uri
?.toHostOrPathOrNull()
?.let { BitwardenString.items_for_uri.asText(it) }
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we format this to match the app guidelines:

                when (data.type) {
                    AutofillSelectionData.Type.IDENTITY -> {
                        BitwardenString.choose_an_identity.asText()
                    }

                    AutofillSelectionData.Type.CARD -> {
                        data.uri
                            ?.toHostOrPathOrNull()
                            ?.let { BitwardenString.select_a_card_for_x.asText(it) }
                    }

                    AutofillSelectionData.Type.LOGIN -> {
                        data.uri
                            ?.toHostOrPathOrNull()
                            ?.let { BitwardenString.items_for_uri.asText(it) }
                    }
                }

}
?: createCredentialRequest
?.relyingPartyIdOrNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class AutofillSelectionDataExtensionsTest {
mapOf(
AutofillSelectionData.Type.CARD to VaultItemListingType.Card,
AutofillSelectionData.Type.LOGIN to VaultItemListingType.Login,
AutofillSelectionData.Type.IDENTITY to VaultItemListingType.Identity,
)
.forEach { (type, expected) ->
assertEquals(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,23 @@ class AutofillSelectionDataExtensionsTest {
)
}

@Test
fun `toDefaultAddTypeContent for an Identity type should return the correct Content`() {
assertEquals(
VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(),
isIndividualVaultDisabled = false,
type = VaultAddEditState.ViewState.Content.ItemType.Identity(),
),
AutofillSelectionData(
type = AutofillSelectionData.Type.IDENTITY,
framework = AutofillSelectionData.Framework.AUTOFILL,
uri = null,
)
.toDefaultAddTypeContent(isIndividualVaultDisabled = false),
)
}

@Test
fun `toDefaultAddTypeContent for a Login type should return the correct Content`() {
every { UUID.randomUUID().toString() } returns "uuid"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,33 @@ class VaultItemListingScreenTest : BitwardenComposeTest() {
.assertIsDisplayed()
}

@Test
fun `topBar title should be displayed for identity autofill selection regardless of the uri`() {
mutableStateFlow.update {
it.copy(
itemListingType = VaultItemListingState.ItemListingType.Vault.Identity,
autofillSelectionData = AUTOFILL_SELECTION_DATA.copy(
type = AutofillSelectionData.Type.IDENTITY,
uri = null,
),
)
}
composeTestRule
.onNodeWithText(text = "Choose an identity")
.assertIsDisplayed()

mutableStateFlow.update {
it.copy(
autofillSelectionData = AUTOFILL_SELECTION_DATA.copy(
type = AutofillSelectionData.Type.IDENTITY,
),
)
}
composeTestRule
.onNodeWithText(text = "Choose an identity")
.assertIsDisplayed()
}

@Test
fun `on overflow item click should display menu`() {
composeTestRule
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2906,6 +2906,76 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
)
}

@Suppress("MaxLineLength")
@Test
fun `vaultDataStateFlow Loaded with items and autofill filtering for Identity should update ViewState to Content with filtered data`() =
runTest {
setupMockUri()

val cipherView1 = createMockCipherListView(
number = 1,
type = CipherListViewType.Identity,
)
val cipherView2 = createMockCipherListView(
number = 2,
type = CipherListViewType.Login(
createMockLoginListView(number = 2),
),
)

val autofillSelectionData = AutofillSelectionData(
type = AutofillSelectionData.Type.IDENTITY,
framework = AutofillSelectionData.Framework.AUTOFILL,
uri = "https://www.test.com",
)
specialCircumstanceManager.specialCircumstance =
SpecialCircumstance.AutofillSelection(
autofillSelectionData = autofillSelectionData,
shouldFinishWhenComplete = true,
)
val dataState = DataState.Loaded(
data = VaultData(
decryptCipherListResult = createMockDecryptCipherListResult(
number = 1,
successes = listOf(cipherView1, cipherView2),
),
folderViewList = listOf(createMockFolderView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
sendViewList = listOf(createMockSendView(number = 1)),
),
)

val viewModel = createVaultItemListingViewModel(
savedStateHandle = createSavedStateHandleWithVaultItemListingType(
vaultItemListingType = VaultItemListingType.Identity,
),
)

mutableVaultDataStateFlow.value = dataState

assertEquals(
createVaultItemListingState(
itemListingType = VaultItemListingState.ItemListingType.Vault.Identity,
viewState = VaultItemListingState.ViewState.Content(
displayCollectionList = emptyList(),
displayItemList = listOf(
createMockDisplayItemForCipher(
number = 1,
cipherType = CipherType.IDENTITY,
subtitle = "mockSubtitle-1",
secondSubtitleTestTag = "PasskeySite",
subtitleTestTag = "PasswordName",
isAutofill = true,
),
),
displayFolderList = emptyList(),
),
)
.copy(autofillSelectionData = autofillSelectionData),
viewModel.stateFlow.value,
)
}

@Suppress("MaxLineLength")
@Test
fun `vaultDataStateFlow Loaded with items and totp data filtering should update ViewState to Content with filtered data`() =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
subtitleTestTag = "CipherSubTitleLabel",
subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_note),
extraIconList = persistentListOf(
IconData.Local(
Expand Down Expand Up @@ -145,7 +145,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
subtitleTestTag = "CipherSubTitleLabel",
subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_payment_card),
extraIconList = persistentListOf(
IconData.Local(
Expand Down Expand Up @@ -197,7 +197,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
subtitleTestTag = "CipherSubTitleLabel",
subtitleTestTag = subtitleTestTag,
Comment thread
david-livefront marked this conversation as resolved.
Comment thread
david-livefront marked this conversation as resolved.
iconData = IconData.Local(BitwardenDrawable.ic_id_card),
extraIconList = persistentListOf(
IconData.Local(
Expand Down Expand Up @@ -225,9 +225,9 @@ fun createMockDisplayItemForCipher(
ListingItemOverflowAction.VaultAction.ArchiveClick(cipherId = "mockId-$number"),
),
optionsTestTag = "CipherOptionsButton",
isAutofill = false,
isCredentialCreation = false,
shouldShowMasterPasswordReprompt = false,
isAutofill = isAutofill,
isCredentialCreation = isCredentialCreation,
shouldShowMasterPasswordReprompt = shouldShowMasterPasswordReprompt,
iconTestTag = "IdentityCipherIcon",
itemType = VaultItemListingState.DisplayItem.ItemType.Vault(type = cipherType),
)
Expand All @@ -241,7 +241,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
subtitleTestTag = "CipherSubTitleLabel",
subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_ssh_key),
extraIconList = persistentListOf(
IconData.Local(
Expand Down Expand Up @@ -285,7 +285,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
subtitleTestTag = "CipherSubTitleLabel",
subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_payment_card),
extraIconList = persistentListOf(
IconData.Local(
Expand Down Expand Up @@ -329,7 +329,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
subtitleTestTag = "CipherSubTitleLabel",
subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_note),
extraIconList = persistentListOf(
IconData.Local(
Expand Down Expand Up @@ -373,7 +373,7 @@ fun createMockDisplayItemForCipher(
secondSubtitle = secondSubtitle,
secondSubtitleTestTag = secondSubtitleTestTag,
subtitle = subtitle,
subtitleTestTag = "CipherSubTitleLabel",
subtitleTestTag = subtitleTestTag,
iconData = IconData.Local(BitwardenDrawable.ic_note),
extraIconList = persistentListOf(
IconData.Local(
Expand Down
1 change: 1 addition & 0 deletions ui/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,7 @@ Do you want to switch to this account?</string>
<item quantity="other">Bitwarden could not decrypt %1$d vault items. Copy and share this error report with customer success to avoid additional data loss.</item>
</plurals>
<string name="select_a_card_for_x">Select a card for %s</string>
<string name="choose_an_identity">Choose an identity</string>
<string name="advanced_options">Advanced options</string>
<string name="more_about_match_detection"><annotation link="moreAboutMatchDetection">More about match detection</annotation></string>
<string name="keep_your_credential_secure">Keep your credentials secure</string>
Expand Down
Loading