mirror of
https://github.com/bitwarden/android.git
synced 2026-10-23 03:23:53 -05:00
[PM-41296] feat: Display Identities vault list when autofilling for an Identity (#7373)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
d70e13393e
commit
5e689276bf
11 files changed
+163
-26
No files matched your search
@@ -23,6 +23,7 @@ data class AutofillSelectionData(
|
||||
enum class Type {
|
||||
CARD,
|
||||
LOGIN,
|
||||
IDENTITY,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
+1
@@ -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
|
||||
}
|
||||
+4
@@ -32,6 +32,10 @@ fun AutofillSelectionData.toDefaultAddTypeContent(
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
AutofillSelectionData.Type.IDENTITY -> {
|
||||
VaultAddEditState.ViewState.Content.ItemType.Identity()
|
||||
}
|
||||
}
|
||||
return VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
|
||||
+30
-12
@@ -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 },
|
||||
failures = emptyList(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
AutofillSelectionData.Type.LOGIN -> {
|
||||
BitwardenString.items_for_uri.asText(it)
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
+1
@@ -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(
|
||||
|
||||
+17
@@ -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"
|
||||
|
||||
+27
@@ -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
|
||||
|
||||
+70
@@ -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`() =
|
||||
|
||||
+10
-10
@@ -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(
|
||||
@@ -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(
|
||||
@@ -197,7 +197,7 @@ fun createMockDisplayItemForCipher(
|
||||
secondSubtitle = secondSubtitle,
|
||||
secondSubtitleTestTag = secondSubtitleTestTag,
|
||||
subtitle = subtitle,
|
||||
subtitleTestTag = "CipherSubTitleLabel",
|
||||
subtitleTestTag = subtitleTestTag,
|
||||
iconData = IconData.Local(BitwardenDrawable.ic_id_card),
|
||||
extraIconList = persistentListOf(
|
||||
IconData.Local(
|
||||
@@ -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),
|
||||
)
|
||||
@@ -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(
|
||||
@@ -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(
|
||||
@@ -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(
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user