mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
PM-9439: Update cipher list item for passkeys (#3422)
This commit is contained in:
parent
a84694b100
commit
eb771e9dfa
20 changed files with 457 additions and 47 deletions
|
@ -15,6 +15,7 @@ import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
||||||
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||||
|
import com.x8bit.bitwarden.data.vault.repository.model.DecryptFido2CredentialAutofillViewResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteFolderResult
|
import com.x8bit.bitwarden.data.vault.repository.model.DeleteFolderResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
||||||
|
@ -144,6 +145,13 @@ interface VaultRepository : CipherManager, VaultLockManager {
|
||||||
*/
|
*/
|
||||||
fun getAuthCodesFlow(): StateFlow<DataState<List<VerificationCodeItem>>>
|
fun getAuthCodesFlow(): StateFlow<DataState<List<VerificationCodeItem>>>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the decrypted list of fido credentials for the current ciphers and user id.
|
||||||
|
*/
|
||||||
|
suspend fun getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList: List<CipherView>,
|
||||||
|
): DecryptFido2CredentialAutofillViewResult
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emits the totp code result flow to listeners.
|
* Emits the totp code result flow to listeners.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -6,7 +6,6 @@ import com.bitwarden.core.InitOrgCryptoRequest
|
||||||
import com.bitwarden.core.InitUserCryptoMethod
|
import com.bitwarden.core.InitUserCryptoMethod
|
||||||
import com.bitwarden.crypto.Kdf
|
import com.bitwarden.crypto.Kdf
|
||||||
import com.bitwarden.exporters.ExportFormat
|
import com.bitwarden.exporters.ExportFormat
|
||||||
import com.bitwarden.fido.Fido2CredentialAutofillView
|
|
||||||
import com.bitwarden.send.Send
|
import com.bitwarden.send.Send
|
||||||
import com.bitwarden.send.SendType
|
import com.bitwarden.send.SendType
|
||||||
import com.bitwarden.send.SendView
|
import com.bitwarden.send.SendView
|
||||||
|
@ -57,6 +56,7 @@ import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
||||||
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||||
|
import com.x8bit.bitwarden.data.vault.repository.model.DecryptFido2CredentialAutofillViewResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteFolderResult
|
import com.x8bit.bitwarden.data.vault.repository.model.DeleteFolderResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
||||||
|
@ -183,6 +183,7 @@ class VaultRepositoryImpl(
|
||||||
) { ciphersData, foldersData, collectionsData, sendsData ->
|
) { ciphersData, foldersData, collectionsData, sendsData ->
|
||||||
VaultData(
|
VaultData(
|
||||||
cipherViewList = ciphersData,
|
cipherViewList = ciphersData,
|
||||||
|
fido2CredentialAutofillViewList = null,
|
||||||
folderViewList = foldersData,
|
folderViewList = foldersData,
|
||||||
collectionViewList = collectionsData,
|
collectionViewList = collectionsData,
|
||||||
sendViewList = sendsData.sendViewList,
|
sendViewList = sendsData.sendViewList,
|
||||||
|
@ -523,6 +524,20 @@ class VaultRepositoryImpl(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList: List<CipherView>,
|
||||||
|
): DecryptFido2CredentialAutofillViewResult {
|
||||||
|
return vaultSdkSource
|
||||||
|
.decryptFido2CredentialAutofillViews(
|
||||||
|
userId = activeUserId ?: return DecryptFido2CredentialAutofillViewResult.Error,
|
||||||
|
cipherViews = cipherViewList.toTypedArray(),
|
||||||
|
)
|
||||||
|
.fold(
|
||||||
|
onFailure = { DecryptFido2CredentialAutofillViewResult.Error },
|
||||||
|
onSuccess = { DecryptFido2CredentialAutofillViewResult.Success(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun emitTotpCodeResult(totpCodeResult: TotpCodeResult) {
|
override fun emitTotpCodeResult(totpCodeResult: TotpCodeResult) {
|
||||||
mutableTotpCodeResultFlow.tryEmit(totpCodeResult)
|
mutableTotpCodeResultFlow.tryEmit(totpCodeResult)
|
||||||
}
|
}
|
||||||
|
@ -861,22 +876,6 @@ class VaultRepositoryImpl(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a filtered list containing elements that match the given [relyingPartyId] and a
|
|
||||||
* credential ID contained in [credentialIds].
|
|
||||||
*/
|
|
||||||
private fun List<Fido2CredentialAutofillView>.filterMatchingCredentials(
|
|
||||||
credentialIds: List<ByteArray>,
|
|
||||||
relyingPartyId: String,
|
|
||||||
): List<Fido2CredentialAutofillView> {
|
|
||||||
val skipCredentialIdFiltering = credentialIds.isEmpty()
|
|
||||||
return filter { fido2CredentialView ->
|
|
||||||
fido2CredentialView.rpId == relyingPartyId &&
|
|
||||||
(skipCredentialIdFiltering ||
|
|
||||||
credentialIds.contains(fido2CredentialView.credentialId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the given [userId] has an associated encrypted PIN key but not a pin-protected user
|
* Checks if the given [userId] has an associated encrypted PIN key but not a pin-protected user
|
||||||
* key. This indicates a scenario in which a user has requested PIN unlocking but requires
|
* key. This indicates a scenario in which a user has requested PIN unlocking but requires
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package com.x8bit.bitwarden.data.vault.repository.model
|
||||||
|
|
||||||
|
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models result of decrypting the fido2 credential autofill views.
|
||||||
|
*/
|
||||||
|
sealed class DecryptFido2CredentialAutofillViewResult {
|
||||||
|
/**
|
||||||
|
* Credentials decrypted successfully.
|
||||||
|
*/
|
||||||
|
data class Success(
|
||||||
|
val fido2CredentialAutofillViews: List<Fido2CredentialAutofillView>,
|
||||||
|
) : DecryptFido2CredentialAutofillViewResult()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic error while decrypting credentials.
|
||||||
|
*/
|
||||||
|
data object Error : DecryptFido2CredentialAutofillViewResult()
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package com.x8bit.bitwarden.data.vault.repository.model
|
package com.x8bit.bitwarden.data.vault.repository.model
|
||||||
|
|
||||||
|
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||||
import com.bitwarden.send.SendView
|
import com.bitwarden.send.SendView
|
||||||
import com.bitwarden.vault.CipherView
|
import com.bitwarden.vault.CipherView
|
||||||
import com.bitwarden.vault.CollectionView
|
import com.bitwarden.vault.CollectionView
|
||||||
|
@ -12,10 +13,12 @@ import com.bitwarden.vault.FolderView
|
||||||
* @param collectionViewList List of decrypted collections.
|
* @param collectionViewList List of decrypted collections.
|
||||||
* @param folderViewList List of decrypted folders.
|
* @param folderViewList List of decrypted folders.
|
||||||
* @param sendViewList List of decrypted sends.
|
* @param sendViewList List of decrypted sends.
|
||||||
|
* @param fido2CredentialAutofillViewList List of decrypted fido 2 credentials.
|
||||||
*/
|
*/
|
||||||
data class VaultData(
|
data class VaultData(
|
||||||
val cipherViewList: List<CipherView>,
|
val cipherViewList: List<CipherView>,
|
||||||
val collectionViewList: List<CollectionView>,
|
val collectionViewList: List<CollectionView>,
|
||||||
val folderViewList: List<FolderView>,
|
val folderViewList: List<FolderView>,
|
||||||
val sendViewList: List<SendView>,
|
val sendViewList: List<SendView>,
|
||||||
|
val fido2CredentialAutofillViewList: List<Fido2CredentialAutofillView>? = null,
|
||||||
)
|
)
|
||||||
|
|
|
@ -53,6 +53,9 @@ import kotlinx.collections.immutable.persistentListOf
|
||||||
* This allows the caller to specify things like padding, size, etc.
|
* This allows the caller to specify things like padding, size, etc.
|
||||||
* @param labelTestTag The optional test tag for the [label].
|
* @param labelTestTag The optional test tag for the [label].
|
||||||
* @param optionsTestTag The optional test tag for the options button.
|
* @param optionsTestTag The optional test tag for the options button.
|
||||||
|
* @param secondSupportingLabel An additional optional text label to display beneath the label and
|
||||||
|
* above the optional supporting label.
|
||||||
|
* @param secondSupportingLabelTestTag The optional test tag for the [secondSupportingLabel].
|
||||||
* @param supportingLabel An optional secondary text label to display beneath the label.
|
* @param supportingLabel An optional secondary text label to display beneath the label.
|
||||||
* @param supportingLabelTestTag The optional test tag for the [supportingLabel].
|
* @param supportingLabelTestTag The optional test tag for the [supportingLabel].
|
||||||
* @param startIconTestTag The optional test tag for the [startIcon].
|
* @param startIconTestTag The optional test tag for the [startIcon].
|
||||||
|
@ -68,6 +71,8 @@ fun BitwardenListItem(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
labelTestTag: String? = null,
|
labelTestTag: String? = null,
|
||||||
optionsTestTag: String? = null,
|
optionsTestTag: String? = null,
|
||||||
|
secondSupportingLabel: String? = null,
|
||||||
|
secondSupportingLabelTestTag: String? = null,
|
||||||
supportingLabel: String? = null,
|
supportingLabel: String? = null,
|
||||||
supportingLabelTestTag: String? = null,
|
supportingLabelTestTag: String? = null,
|
||||||
startIconTestTag: String? = null,
|
startIconTestTag: String? = null,
|
||||||
|
@ -124,6 +129,17 @@ fun BitwardenListItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secondSupportingLabel?.let { secondSupportLabel ->
|
||||||
|
Text(
|
||||||
|
text = secondSupportLabel,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.semantics {
|
||||||
|
secondSupportingLabelTestTag?.let { testTag = it }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
supportingLabel?.let { supportLabel ->
|
supportingLabel?.let { supportLabel ->
|
||||||
Text(
|
Text(
|
||||||
text = supportLabel,
|
text = supportLabel,
|
||||||
|
|
|
@ -222,6 +222,7 @@ private fun CipherView.toIconData(
|
||||||
login?.uris.toLoginIconData(
|
login?.uris.toLoginIconData(
|
||||||
baseIconUrl = baseIconUrl,
|
baseIconUrl = baseIconUrl,
|
||||||
isIconLoadingDisabled = isIconLoadingDisabled,
|
isIconLoadingDisabled = isIconLoadingDisabled,
|
||||||
|
usePasskeyDefaultIcon = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,8 @@ fun VaultItemListingContent(
|
||||||
startIconTestTag = it.iconTestTag,
|
startIconTestTag = it.iconTestTag,
|
||||||
label = it.title,
|
label = it.title,
|
||||||
labelTestTag = it.titleTestTag,
|
labelTestTag = it.titleTestTag,
|
||||||
|
secondSupportingLabel = it.secondSubtitle,
|
||||||
|
secondSupportingLabelTestTag = it.secondSubtitleTestTag,
|
||||||
supportingLabel = it.subtitle,
|
supportingLabel = it.subtitle,
|
||||||
supportingLabelTestTag = it.subtitleTestTag,
|
supportingLabelTestTag = it.subtitleTestTag,
|
||||||
optionsTestTag = it.optionsTestTag,
|
optionsTestTag = it.optionsTestTag,
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.x8bit.bitwarden.ui.vault.feature.itemlisting
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
|
||||||
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
|
import com.x8bit.bitwarden.data.auth.repository.model.ValidatePasswordResult
|
||||||
|
@ -12,6 +13,7 @@ import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2RegisterCredentialResu
|
||||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2ValidateOriginResult
|
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2ValidateOriginResult
|
||||||
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
|
import com.x8bit.bitwarden.data.autofill.manager.AutofillSelectionManager
|
||||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||||
|
import com.x8bit.bitwarden.data.autofill.util.isActiveWithFido2Credentials
|
||||||
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
import com.x8bit.bitwarden.data.platform.manager.PolicyManager
|
||||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||||
import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager
|
import com.x8bit.bitwarden.data.platform.manager.ciphermatching.CipherMatchingManager
|
||||||
|
@ -28,6 +30,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.map
|
||||||
import com.x8bit.bitwarden.data.platform.util.getFido2RpIdOrNull
|
import com.x8bit.bitwarden.data.platform.util.getFido2RpIdOrNull
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.PolicyTypeJson
|
||||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||||
|
import com.x8bit.bitwarden.data.vault.repository.model.DecryptFido2CredentialAutofillViewResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||||
|
@ -857,6 +860,8 @@ class VaultItemListingViewModel @Inject constructor(
|
||||||
isIconLoadingDisabled = state.isIconLoadingDisabled,
|
isIconLoadingDisabled = state.isIconLoadingDisabled,
|
||||||
autofillSelectionData = state.autofillSelectionData,
|
autofillSelectionData = state.autofillSelectionData,
|
||||||
fido2CreationData = state.fido2CredentialRequest,
|
fido2CreationData = state.fido2CredentialRequest,
|
||||||
|
fido2CredentialAutofillViews = vaultData
|
||||||
|
.fido2CredentialAutofillViewList,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -899,6 +904,7 @@ class VaultItemListingViewModel @Inject constructor(
|
||||||
ciphers = vaultData.cipherViewList,
|
ciphers = vaultData.cipherViewList,
|
||||||
matchUri = matchUri,
|
matchUri = matchUri,
|
||||||
),
|
),
|
||||||
|
fido2CredentialAutofillViewList = vaultData.toFido2CredentialAutofillViews(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -919,9 +925,24 @@ class VaultItemListingViewModel @Inject constructor(
|
||||||
ciphers = vaultData.cipherViewList,
|
ciphers = vaultData.cipherViewList,
|
||||||
matchUri = matchUri,
|
matchUri = matchUri,
|
||||||
),
|
),
|
||||||
|
fido2CredentialAutofillViewList = vaultData.toFido2CredentialAutofillViews(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt and filter the fido 2 autofill credentials.
|
||||||
|
*/
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
private suspend fun VaultData.toFido2CredentialAutofillViews(): List<Fido2CredentialAutofillView>? =
|
||||||
|
(vaultRepository
|
||||||
|
.getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList = this
|
||||||
|
.cipherViewList
|
||||||
|
.filter { it.isActiveWithFido2Credentials },
|
||||||
|
)
|
||||||
|
as? DecryptFido2CredentialAutofillViewResult.Success)
|
||||||
|
?.fido2CredentialAutofillViews
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1089,6 +1110,8 @@ data class VaultItemListingState(
|
||||||
* @property id the id of the item.
|
* @property id the id of the item.
|
||||||
* @property title title of the item.
|
* @property title title of the item.
|
||||||
* @property titleTestTag The test tag associated with the [title].
|
* @property titleTestTag The test tag associated with the [title].
|
||||||
|
* @property secondSubtitle The second subtitle of the item (nullable).
|
||||||
|
* @property secondSubtitleTestTag The test tag associated with the [secondSubtitle].
|
||||||
* @property subtitle subtitle of the item (nullable).
|
* @property subtitle subtitle of the item (nullable).
|
||||||
* @property subtitleTestTag The test tag associated with the [subtitle].
|
* @property subtitleTestTag The test tag associated with the [subtitle].
|
||||||
* @property iconData data for the icon to be displayed (nullable).
|
* @property iconData data for the icon to be displayed (nullable).
|
||||||
|
@ -1104,6 +1127,8 @@ data class VaultItemListingState(
|
||||||
val id: String,
|
val id: String,
|
||||||
val title: String,
|
val title: String,
|
||||||
val titleTestTag: String,
|
val titleTestTag: String,
|
||||||
|
val secondSubtitle: String?,
|
||||||
|
val secondSubtitleTestTag: String?,
|
||||||
val subtitle: String?,
|
val subtitle: String?,
|
||||||
val subtitleTestTag: String,
|
val subtitleTestTag: String,
|
||||||
val iconData: IconData,
|
val iconData: IconData,
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package com.x8bit.bitwarden.ui.vault.feature.itemlisting.util
|
package com.x8bit.bitwarden.ui.vault.feature.itemlisting.util
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||||
import com.bitwarden.send.SendType
|
import com.bitwarden.send.SendType
|
||||||
import com.bitwarden.send.SendView
|
import com.bitwarden.send.SendView
|
||||||
import com.bitwarden.vault.CipherRepromptType
|
import com.bitwarden.vault.CipherRepromptType
|
||||||
|
@ -13,6 +14,7 @@ import com.bitwarden.vault.FolderView
|
||||||
import com.x8bit.bitwarden.R
|
import com.x8bit.bitwarden.R
|
||||||
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialRequest
|
import com.x8bit.bitwarden.data.autofill.fido2.model.Fido2CredentialRequest
|
||||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||||
|
import com.x8bit.bitwarden.data.autofill.util.isActiveWithFido2Credentials
|
||||||
import com.x8bit.bitwarden.data.platform.util.subtitle
|
import com.x8bit.bitwarden.data.platform.util.subtitle
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||||
|
@ -101,6 +103,7 @@ fun VaultData.toViewState(
|
||||||
isIconLoadingDisabled: Boolean,
|
isIconLoadingDisabled: Boolean,
|
||||||
autofillSelectionData: AutofillSelectionData?,
|
autofillSelectionData: AutofillSelectionData?,
|
||||||
fido2CreationData: Fido2CredentialRequest?,
|
fido2CreationData: Fido2CredentialRequest?,
|
||||||
|
fido2CredentialAutofillViews: List<Fido2CredentialAutofillView>?,
|
||||||
): VaultItemListingState.ViewState {
|
): VaultItemListingState.ViewState {
|
||||||
val filteredCipherViewList = cipherViewList
|
val filteredCipherViewList = cipherViewList
|
||||||
.filter { cipherView ->
|
.filter { cipherView ->
|
||||||
|
@ -129,6 +132,7 @@ fun VaultData.toViewState(
|
||||||
isIconLoadingDisabled = isIconLoadingDisabled,
|
isIconLoadingDisabled = isIconLoadingDisabled,
|
||||||
isAutofill = autofillSelectionData != null,
|
isAutofill = autofillSelectionData != null,
|
||||||
isFido2Creation = fido2CreationData != null,
|
isFido2Creation = fido2CreationData != null,
|
||||||
|
fido2CredentialAutofillViews = fido2CredentialAutofillViews,
|
||||||
),
|
),
|
||||||
displayFolderList = folderList.map { folderView ->
|
displayFolderList = folderList.map { folderView ->
|
||||||
VaultItemListingState.FolderDisplayItem(
|
VaultItemListingState.FolderDisplayItem(
|
||||||
|
@ -259,12 +263,14 @@ fun VaultItemListingState.ItemListingType.updateWithAdditionalDataIfNecessary(
|
||||||
is VaultItemListingState.ItemListingType.Send.SendText -> this
|
is VaultItemListingState.ItemListingType.Send.SendText -> this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
private fun List<CipherView>.toDisplayItemList(
|
private fun List<CipherView>.toDisplayItemList(
|
||||||
baseIconUrl: String,
|
baseIconUrl: String,
|
||||||
hasMasterPassword: Boolean,
|
hasMasterPassword: Boolean,
|
||||||
isIconLoadingDisabled: Boolean,
|
isIconLoadingDisabled: Boolean,
|
||||||
isAutofill: Boolean,
|
isAutofill: Boolean,
|
||||||
isFido2Creation: Boolean,
|
isFido2Creation: Boolean,
|
||||||
|
fido2CredentialAutofillViews: List<Fido2CredentialAutofillView>?,
|
||||||
): List<VaultItemListingState.DisplayItem> =
|
): List<VaultItemListingState.DisplayItem> =
|
||||||
this.map {
|
this.map {
|
||||||
it.toDisplayItem(
|
it.toDisplayItem(
|
||||||
|
@ -273,6 +279,10 @@ private fun List<CipherView>.toDisplayItemList(
|
||||||
isIconLoadingDisabled = isIconLoadingDisabled,
|
isIconLoadingDisabled = isIconLoadingDisabled,
|
||||||
isAutofill = isAutofill,
|
isAutofill = isAutofill,
|
||||||
isFido2Creation = isFido2Creation,
|
isFido2Creation = isFido2Creation,
|
||||||
|
fido2CredentialAutofillView = fido2CredentialAutofillViews
|
||||||
|
?.firstOrNull { fido2CredentialAutofillView ->
|
||||||
|
fido2CredentialAutofillView.cipherId == it.id
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,32 +297,55 @@ private fun List<SendView>.toDisplayItemList(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
private fun CipherView.toDisplayItem(
|
private fun CipherView.toDisplayItem(
|
||||||
baseIconUrl: String,
|
baseIconUrl: String,
|
||||||
hasMasterPassword: Boolean,
|
hasMasterPassword: Boolean,
|
||||||
isIconLoadingDisabled: Boolean,
|
isIconLoadingDisabled: Boolean,
|
||||||
isAutofill: Boolean,
|
isAutofill: Boolean,
|
||||||
isFido2Creation: Boolean,
|
isFido2Creation: Boolean,
|
||||||
|
fido2CredentialAutofillView: Fido2CredentialAutofillView?,
|
||||||
): VaultItemListingState.DisplayItem =
|
): VaultItemListingState.DisplayItem =
|
||||||
VaultItemListingState.DisplayItem(
|
VaultItemListingState.DisplayItem(
|
||||||
id = id.orEmpty(),
|
id = id.orEmpty(),
|
||||||
title = name,
|
title = name,
|
||||||
titleTestTag = "CipherNameLabel",
|
titleTestTag = "CipherNameLabel",
|
||||||
subtitle = subtitle,
|
secondSubtitle = this.toSecondSubtitle(fido2CredentialAutofillView?.rpId),
|
||||||
subtitleTestTag = "CipherSubTitleLabel",
|
secondSubtitleTestTag = "PasskeySite",
|
||||||
|
subtitle = this.subtitle,
|
||||||
|
subtitleTestTag = this.toSubtitleTestTag(
|
||||||
|
isAutofill = isAutofill,
|
||||||
|
isFido2Creation = isFido2Creation,
|
||||||
|
),
|
||||||
iconData = this.toIconData(
|
iconData = this.toIconData(
|
||||||
baseIconUrl = baseIconUrl,
|
baseIconUrl = baseIconUrl,
|
||||||
isIconLoadingDisabled = isIconLoadingDisabled,
|
isIconLoadingDisabled = isIconLoadingDisabled,
|
||||||
|
usePasskeyDefaultIcon = (isAutofill || isFido2Creation) &&
|
||||||
|
this.isActiveWithFido2Credentials,
|
||||||
),
|
),
|
||||||
iconTestTag = toIconTestTag(),
|
iconTestTag = this.toIconTestTag(),
|
||||||
extraIconList = toLabelIcons(),
|
extraIconList = this.toLabelIcons(),
|
||||||
overflowOptions = toOverflowActions(hasMasterPassword = hasMasterPassword),
|
overflowOptions = this.toOverflowActions(hasMasterPassword = hasMasterPassword),
|
||||||
optionsTestTag = "CipherOptionsButton",
|
optionsTestTag = "CipherOptionsButton",
|
||||||
isAutofill = isAutofill,
|
isAutofill = isAutofill,
|
||||||
isFido2Creation = isFido2Creation,
|
isFido2Creation = isFido2Creation,
|
||||||
shouldShowMasterPasswordReprompt = reprompt == CipherRepromptType.PASSWORD,
|
shouldShowMasterPasswordReprompt = reprompt == CipherRepromptType.PASSWORD,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private fun CipherView.toSecondSubtitle(fido2CredentialRpId: String?): String? =
|
||||||
|
fido2CredentialRpId
|
||||||
|
?.takeIf { this.type == CipherType.LOGIN && it.isNotEmpty() && it != this.name }
|
||||||
|
|
||||||
|
private fun CipherView.toSubtitleTestTag(
|
||||||
|
isAutofill: Boolean,
|
||||||
|
isFido2Creation: Boolean,
|
||||||
|
): String =
|
||||||
|
if ((isAutofill || isFido2Creation)) {
|
||||||
|
if (this.isActiveWithFido2Credentials) "PasskeyName" else "PasswordName"
|
||||||
|
} else {
|
||||||
|
"CipherSubTitleLabel"
|
||||||
|
}
|
||||||
|
|
||||||
private fun CipherView.toIconTestTag(): String =
|
private fun CipherView.toIconTestTag(): String =
|
||||||
when (type) {
|
when (type) {
|
||||||
CipherType.LOGIN -> "LoginCipherIcon"
|
CipherType.LOGIN -> "LoginCipherIcon"
|
||||||
|
@ -324,12 +357,14 @@ private fun CipherView.toIconTestTag(): String =
|
||||||
private fun CipherView.toIconData(
|
private fun CipherView.toIconData(
|
||||||
baseIconUrl: String,
|
baseIconUrl: String,
|
||||||
isIconLoadingDisabled: Boolean,
|
isIconLoadingDisabled: Boolean,
|
||||||
|
usePasskeyDefaultIcon: Boolean,
|
||||||
): IconData {
|
): IconData {
|
||||||
return when (this.type) {
|
return when (this.type) {
|
||||||
CipherType.LOGIN -> {
|
CipherType.LOGIN -> {
|
||||||
login?.uris.toLoginIconData(
|
login?.uris.toLoginIconData(
|
||||||
baseIconUrl = baseIconUrl,
|
baseIconUrl = baseIconUrl,
|
||||||
isIconLoadingDisabled = isIconLoadingDisabled,
|
isIconLoadingDisabled = isIconLoadingDisabled,
|
||||||
|
usePasskeyDefaultIcon = usePasskeyDefaultIcon,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,6 +382,8 @@ private fun SendView.toDisplayItem(
|
||||||
id = id.orEmpty(),
|
id = id.orEmpty(),
|
||||||
title = name,
|
title = name,
|
||||||
titleTestTag = "SendNameLabel",
|
titleTestTag = "SendNameLabel",
|
||||||
|
secondSubtitle = null,
|
||||||
|
secondSubtitleTestTag = null,
|
||||||
subtitle = deletionDate.toFormattedPattern(DELETION_DATE_PATTERN, clock),
|
subtitle = deletionDate.toFormattedPattern(DELETION_DATE_PATTERN, clock),
|
||||||
subtitleTestTag = "SendDateLabel",
|
subtitleTestTag = "SendDateLabel",
|
||||||
iconData = IconData.Local(
|
iconData = IconData.Local(
|
||||||
|
|
|
@ -146,13 +146,18 @@ fun VaultData.toViewState(
|
||||||
fun List<LoginUriView>?.toLoginIconData(
|
fun List<LoginUriView>?.toLoginIconData(
|
||||||
isIconLoadingDisabled: Boolean,
|
isIconLoadingDisabled: Boolean,
|
||||||
baseIconUrl: String,
|
baseIconUrl: String,
|
||||||
|
usePasskeyDefaultIcon: Boolean,
|
||||||
): IconData {
|
): IconData {
|
||||||
val localIconData = IconData.Local(R.drawable.ic_login_item)
|
val defaultIconRes = if (usePasskeyDefaultIcon) {
|
||||||
|
R.drawable.ic_login_item_passkey
|
||||||
|
} else {
|
||||||
|
R.drawable.ic_login_item
|
||||||
|
}
|
||||||
|
|
||||||
var uri = this
|
var uri = this
|
||||||
?.map { it.uri }
|
?.map { it.uri }
|
||||||
?.firstOrNull { uri -> uri?.contains(".") == true }
|
?.firstOrNull { uri -> uri?.contains(".") == true }
|
||||||
?: return localIconData
|
?: return IconData.Local(defaultIconRes)
|
||||||
|
|
||||||
if (uri.startsWith(ANDROID_URI)) {
|
if (uri.startsWith(ANDROID_URI)) {
|
||||||
return IconData.Local(R.drawable.ic_android)
|
return IconData.Local(R.drawable.ic_android)
|
||||||
|
@ -163,7 +168,7 @@ fun List<LoginUriView>?.toLoginIconData(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isIconLoadingDisabled) {
|
if (isIconLoadingDisabled) {
|
||||||
return localIconData
|
return IconData.Local(defaultIconRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!uri.contains("://")) {
|
if (!uri.contains("://")) {
|
||||||
|
@ -177,7 +182,7 @@ fun List<LoginUriView>?.toLoginIconData(
|
||||||
|
|
||||||
return IconData.Network(
|
return IconData.Network(
|
||||||
uri = url,
|
uri = url,
|
||||||
fallbackIconRes = R.drawable.ic_login_item,
|
fallbackIconRes = defaultIconRes,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,6 +204,7 @@ private fun CipherView.toVaultItemOrNull(
|
||||||
startIcon = login?.uris.toLoginIconData(
|
startIcon = login?.uris.toLoginIconData(
|
||||||
isIconLoadingDisabled = isIconLoadingDisabled,
|
isIconLoadingDisabled = isIconLoadingDisabled,
|
||||||
baseIconUrl = baseIconUrl,
|
baseIconUrl = baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = false,
|
||||||
),
|
),
|
||||||
overflowOptions = toOverflowActions(hasMasterPassword = hasMasterPassword),
|
overflowOptions = toOverflowActions(hasMasterPassword = hasMasterPassword),
|
||||||
extraIconList = toLabelIcons(),
|
extraIconList = toLabelIcons(),
|
||||||
|
|
|
@ -299,6 +299,7 @@ class VerificationCodeViewModel @Inject constructor(
|
||||||
startIcon = item.uriLoginViewList.toLoginIconData(
|
startIcon = item.uriLoginViewList.toLoginIconData(
|
||||||
baseIconUrl = state.baseIconUrl,
|
baseIconUrl = state.baseIconUrl,
|
||||||
isIconLoadingDisabled = state.isIconLoadingDisabled,
|
isIconLoadingDisabled = state.isIconLoadingDisabled,
|
||||||
|
usePasskeyDefaultIcon = false,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
7
app/src/main/res/drawable/ic_login_item_passkey.xml
Normal file
7
app/src/main/res/drawable/ic_login_item_passkey.xml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="23" android:viewportWidth="24" android:width="25.043478dp">
|
||||||
|
|
||||||
|
<path android:fillColor="#175DDC" android:pathData="M4.019,18.894C3.344,18.894 2.797,18.347 2.797,17.672C2.797,16.997 3.344,16.45 4.019,16.45C4.694,16.45 5.241,16.997 5.241,17.672C5.241,18.347 4.694,18.894 4.019,18.894Z"/>
|
||||||
|
|
||||||
|
<path android:fillColor="#175DDC" android:fillType="evenOdd" android:pathData="M7.872,11.322C6.193,10.258 5.079,8.384 5.079,6.25C5.079,2.936 7.765,0.25 11.078,0.25C14.392,0.25 17.079,2.936 17.079,6.25C17.079,8.384 15.964,10.258 14.285,11.322C15.731,11.858 17.002,12.745 17.991,13.879C18.256,14.174 18.489,14.441 18.65,14.669L21.328,14.667L23.991,17.229L20.241,20.575L18.741,19.075L17.241,20.575L15.741,19.075L14.241,20.515L9.761,20.509C8.767,21.896 7.107,22.788 5.246,22.749C2.281,22.687 -0.071,20.287 -0.008,17.388C0.056,14.489 2.51,12.189 5.475,12.251C5.643,12.255 5.81,12.266 5.975,12.284C6.123,12.188 6.291,12.085 6.482,11.975C6.923,11.72 7.387,11.502 7.872,11.322ZM9.888,14.677L16.663,14.671C16.465,14.454 16.201,14.19 15.808,13.879C14.51,12.859 12.866,12.25 11.078,12.25C9.968,12.25 8.914,12.485 7.963,12.907C8.746,13.332 9.408,13.943 9.888,14.677ZM6.579,6.25C6.579,8.735 8.593,10.75 11.078,10.75C13.564,10.75 15.578,8.735 15.578,6.25C15.578,3.765 13.564,1.75 11.078,1.75C8.593,1.75 6.579,3.765 6.579,6.25ZM8.991,19.008L8.542,19.635C7.829,20.629 6.632,21.277 5.277,21.249C3.11,21.204 1.448,19.459 1.492,17.421C1.536,15.381 3.275,13.706 5.443,13.751C6.798,13.779 7.964,14.476 8.632,15.497L9.077,16.178L20.724,16.167L21.785,17.187L20.3,18.512L18.741,16.954L17.241,18.454L15.763,16.975L13.639,19.014L8.991,19.008Z"/>
|
||||||
|
|
||||||
|
</vector>
|
|
@ -1,5 +1,6 @@
|
||||||
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
|
||||||
|
|
||||||
|
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||||
import com.bitwarden.vault.AttachmentView
|
import com.bitwarden.vault.AttachmentView
|
||||||
import com.bitwarden.vault.CardView
|
import com.bitwarden.vault.CardView
|
||||||
import com.bitwarden.vault.CipherRepromptType
|
import com.bitwarden.vault.CipherRepromptType
|
||||||
|
@ -138,6 +139,21 @@ fun createMockSdkFido2Credential(
|
||||||
creationDate = clock.instant(),
|
creationDate = clock.instant(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a mock [Fido2CredentialAutofillView] with a given [number] and optional [cipherId].
|
||||||
|
*/
|
||||||
|
fun createMockFido2CredentialAutofillView(
|
||||||
|
number: Int,
|
||||||
|
cipherId: String? = null,
|
||||||
|
): Fido2CredentialAutofillView =
|
||||||
|
Fido2CredentialAutofillView(
|
||||||
|
credentialId = "mockCredentialId-$number".encodeToByteArray(),
|
||||||
|
cipherId = cipherId ?: "mockCipherId-$number",
|
||||||
|
rpId = "mockRpId-$number",
|
||||||
|
userNameForUi = "mockUserNameForUi-$number",
|
||||||
|
userHandle = "mockUserHandle-$number".encodeToByteArray(),
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a mock [LoginUriView] with a given [number].
|
* Create a mock [LoginUriView] with a given [number].
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,6 +8,7 @@ import com.bitwarden.core.DateTime
|
||||||
import com.bitwarden.core.InitOrgCryptoRequest
|
import com.bitwarden.core.InitOrgCryptoRequest
|
||||||
import com.bitwarden.core.InitUserCryptoMethod
|
import com.bitwarden.core.InitUserCryptoMethod
|
||||||
import com.bitwarden.exporters.ExportFormat
|
import com.bitwarden.exporters.ExportFormat
|
||||||
|
import com.bitwarden.fido.Fido2CredentialAutofillView
|
||||||
import com.bitwarden.send.SendType
|
import com.bitwarden.send.SendType
|
||||||
import com.bitwarden.send.SendView
|
import com.bitwarden.send.SendView
|
||||||
import com.bitwarden.vault.CipherView
|
import com.bitwarden.vault.CipherView
|
||||||
|
@ -77,6 +78,7 @@ import com.x8bit.bitwarden.data.vault.manager.VaultLockManager
|
||||||
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
import com.x8bit.bitwarden.data.vault.manager.model.VerificationCodeItem
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
import com.x8bit.bitwarden.data.vault.repository.model.CreateFolderResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
import com.x8bit.bitwarden.data.vault.repository.model.CreateSendResult
|
||||||
|
import com.x8bit.bitwarden.data.vault.repository.model.DecryptFido2CredentialAutofillViewResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteFolderResult
|
import com.x8bit.bitwarden.data.vault.repository.model.DeleteFolderResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
import com.x8bit.bitwarden.data.vault.repository.model.DomainsData
|
||||||
|
@ -4166,6 +4168,94 @@ class VaultRepositoryTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `getDecryptedFido2CredentialAutofillViews should return error when userId not found`() =
|
||||||
|
runTest {
|
||||||
|
fakeAuthDiskSource.userState = null
|
||||||
|
|
||||||
|
val expected = DecryptFido2CredentialAutofillViewResult.Error
|
||||||
|
val result = vaultRepository
|
||||||
|
.getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList = listOf(createMockCipherView(number = 1)),
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
expected,
|
||||||
|
result,
|
||||||
|
)
|
||||||
|
coVerify(exactly = 0) {
|
||||||
|
vaultSdkSource.decryptFido2CredentialAutofillViews(any(), any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `getDecryptedFido2CredentialAutofillViews should return error when decryption fails`() =
|
||||||
|
runTest {
|
||||||
|
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||||
|
val cipherViewList = listOf(createMockCipherView(number = 1))
|
||||||
|
coEvery {
|
||||||
|
vaultSdkSource.decryptFido2CredentialAutofillViews(
|
||||||
|
userId = MOCK_USER_STATE.activeUserId,
|
||||||
|
cipherViews = cipherViewList.toTypedArray(),
|
||||||
|
)
|
||||||
|
} returns Throwable().asFailure()
|
||||||
|
|
||||||
|
turbineScope {
|
||||||
|
val expected = DecryptFido2CredentialAutofillViewResult.Error
|
||||||
|
val result = vaultRepository
|
||||||
|
.getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList = cipherViewList,
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
expected,
|
||||||
|
result,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
coVerify {
|
||||||
|
vaultSdkSource.decryptFido2CredentialAutofillViews(
|
||||||
|
userId = MOCK_USER_STATE.activeUserId,
|
||||||
|
cipherViews = cipherViewList.toTypedArray(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `getDecryptedFido2CredentialAutofillViews should return correct results when decryption succeeds`() =
|
||||||
|
runTest {
|
||||||
|
fakeAuthDiskSource.userState = MOCK_USER_STATE
|
||||||
|
val cipherViewList = listOf(createMockCipherView(number = 1))
|
||||||
|
val autofillViewList = mockk<List<Fido2CredentialAutofillView>>()
|
||||||
|
val expected = DecryptFido2CredentialAutofillViewResult.Success(
|
||||||
|
fido2CredentialAutofillViews = autofillViewList,
|
||||||
|
)
|
||||||
|
coEvery {
|
||||||
|
vaultSdkSource.decryptFido2CredentialAutofillViews(
|
||||||
|
userId = MOCK_USER_STATE.activeUserId,
|
||||||
|
cipherViews = cipherViewList.toTypedArray(),
|
||||||
|
)
|
||||||
|
} returns autofillViewList.asSuccess()
|
||||||
|
|
||||||
|
turbineScope {
|
||||||
|
val result = vaultRepository
|
||||||
|
.getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList = cipherViewList,
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
expected,
|
||||||
|
result,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
coVerify {
|
||||||
|
vaultSdkSource.decryptFido2CredentialAutofillViews(
|
||||||
|
userId = MOCK_USER_STATE.activeUserId,
|
||||||
|
cipherViews = cipherViewList.toTypedArray(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//region Helper functions
|
//region Helper functions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1536,6 +1536,8 @@ private fun createDisplayItem(number: Int): VaultItemListingState.DisplayItem =
|
||||||
id = "mockId-$number",
|
id = "mockId-$number",
|
||||||
title = "mockTitle-$number",
|
title = "mockTitle-$number",
|
||||||
titleTestTag = "SendNameLabel",
|
titleTestTag = "SendNameLabel",
|
||||||
|
secondSubtitle = null,
|
||||||
|
secondSubtitleTestTag = null,
|
||||||
subtitle = "mockSubtitle-$number",
|
subtitle = "mockSubtitle-$number",
|
||||||
subtitleTestTag = "SendDateLabel",
|
subtitleTestTag = "SendDateLabel",
|
||||||
iconData = IconData.Local(R.drawable.ic_card_item),
|
iconData = IconData.Local(R.drawable.ic_card_item),
|
||||||
|
@ -1580,6 +1582,8 @@ private fun createCipherDisplayItem(number: Int): VaultItemListingState.DisplayI
|
||||||
id = "mockId-$number",
|
id = "mockId-$number",
|
||||||
title = "mockTitle-$number",
|
title = "mockTitle-$number",
|
||||||
titleTestTag = "CipherNameLabel",
|
titleTestTag = "CipherNameLabel",
|
||||||
|
secondSubtitle = null,
|
||||||
|
secondSubtitleTestTag = null,
|
||||||
subtitle = "mockSubtitle-$number",
|
subtitle = "mockSubtitle-$number",
|
||||||
subtitleTestTag = "CipherSubTitleLabel",
|
subtitleTestTag = "CipherSubTitleLabel",
|
||||||
iconData = IconData.Local(R.drawable.ic_vault),
|
iconData = IconData.Local(R.drawable.ic_vault),
|
||||||
|
|
|
@ -36,6 +36,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionV
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||||
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
|
||||||
|
import com.x8bit.bitwarden.data.vault.repository.model.DecryptFido2CredentialAutofillViewResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
import com.x8bit.bitwarden.data.vault.repository.model.DeleteSendResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
import com.x8bit.bitwarden.data.vault.repository.model.RemovePasswordSendResult
|
||||||
|
@ -44,6 +45,7 @@ import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.concat
|
import com.x8bit.bitwarden.ui.platform.base.util.concat
|
||||||
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
import com.x8bit.bitwarden.ui.platform.components.model.AccountSummary
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||||
import com.x8bit.bitwarden.ui.platform.feature.search.model.SearchType
|
import com.x8bit.bitwarden.ui.platform.feature.search.model.SearchType
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.model.ListingItemOverflowAction
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.util.createMockDisplayItemForCipher
|
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.util.createMockDisplayItemForCipher
|
||||||
|
@ -284,6 +286,11 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
runTest {
|
runTest {
|
||||||
setupMockUri()
|
setupMockUri()
|
||||||
val cipherView = createMockCipherView(number = 1)
|
val cipherView = createMockCipherView(number = 1)
|
||||||
|
coEvery {
|
||||||
|
vaultRepository.getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList = listOf(cipherView),
|
||||||
|
)
|
||||||
|
} returns DecryptFido2CredentialAutofillViewResult.Error
|
||||||
specialCircumstanceManager.specialCircumstance =
|
specialCircumstanceManager.specialCircumstance =
|
||||||
SpecialCircumstance.AutofillSelection(
|
SpecialCircumstance.AutofillSelection(
|
||||||
autofillSelectionData = AutofillSelectionData(
|
autofillSelectionData = AutofillSelectionData(
|
||||||
|
@ -309,6 +316,11 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
awaitItem(),
|
awaitItem(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
coVerify {
|
||||||
|
vaultRepository.getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList = listOf(cipherView),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -354,7 +366,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
viewState = VaultItemListingState.ViewState.Content(
|
viewState = VaultItemListingState.ViewState.Content(
|
||||||
displayCollectionList = emptyList(),
|
displayCollectionList = emptyList(),
|
||||||
displayItemList = listOf(
|
displayItemList = listOf(
|
||||||
createMockDisplayItemForCipher(number = 1),
|
createMockDisplayItemForCipher(number = 1)
|
||||||
|
.copy(secondSubtitleTestTag = "PasskeySite"),
|
||||||
),
|
),
|
||||||
displayFolderList = emptyList(),
|
displayFolderList = emptyList(),
|
||||||
),
|
),
|
||||||
|
@ -404,7 +417,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
viewState = VaultItemListingState.ViewState.Content(
|
viewState = VaultItemListingState.ViewState.Content(
|
||||||
displayCollectionList = emptyList(),
|
displayCollectionList = emptyList(),
|
||||||
displayItemList = listOf(
|
displayItemList = listOf(
|
||||||
createMockDisplayItemForCipher(number = 1),
|
createMockDisplayItemForCipher(number = 1)
|
||||||
|
.copy(secondSubtitleTestTag = "PasskeySite"),
|
||||||
),
|
),
|
||||||
displayFolderList = emptyList(),
|
displayFolderList = emptyList(),
|
||||||
),
|
),
|
||||||
|
@ -928,7 +942,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
viewState = VaultItemListingState.ViewState.Content(
|
viewState = VaultItemListingState.ViewState.Content(
|
||||||
displayCollectionList = emptyList(),
|
displayCollectionList = emptyList(),
|
||||||
displayItemList = listOf(
|
displayItemList = listOf(
|
||||||
createMockDisplayItemForCipher(number = 1),
|
createMockDisplayItemForCipher(number = 1)
|
||||||
|
.copy(secondSubtitleTestTag = "PasskeySite"),
|
||||||
),
|
),
|
||||||
displayFolderList = emptyList(),
|
displayFolderList = emptyList(),
|
||||||
),
|
),
|
||||||
|
@ -946,6 +961,12 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
val cipherView1 = createMockCipherView(number = 1)
|
val cipherView1 = createMockCipherView(number = 1)
|
||||||
val cipherView2 = createMockCipherView(number = 2)
|
val cipherView2 = createMockCipherView(number = 2)
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
vaultRepository.getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList = listOf(cipherView1, cipherView2),
|
||||||
|
)
|
||||||
|
} returns DecryptFido2CredentialAutofillViewResult.Success(emptyList())
|
||||||
|
|
||||||
// Set up the data to be filtered
|
// Set up the data to be filtered
|
||||||
mockFilteredCiphers = listOf(cipherView1)
|
mockFilteredCiphers = listOf(cipherView1)
|
||||||
|
|
||||||
|
@ -976,7 +997,15 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
viewState = VaultItemListingState.ViewState.Content(
|
viewState = VaultItemListingState.ViewState.Content(
|
||||||
displayCollectionList = emptyList(),
|
displayCollectionList = emptyList(),
|
||||||
displayItemList = listOf(
|
displayItemList = listOf(
|
||||||
createMockDisplayItemForCipher(number = 1).copy(isAutofill = true),
|
createMockDisplayItemForCipher(number = 1).copy(
|
||||||
|
secondSubtitleTestTag = "PasskeySite",
|
||||||
|
subtitleTestTag = "PasskeyName",
|
||||||
|
iconData = IconData.Network(
|
||||||
|
uri = "https://vault.bitwarden.com/icons/www.mockuri.com/icon.png",
|
||||||
|
fallbackIconRes = R.drawable.ic_login_item_passkey,
|
||||||
|
),
|
||||||
|
isAutofill = true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
displayFolderList = emptyList(),
|
displayFolderList = emptyList(),
|
||||||
),
|
),
|
||||||
|
@ -987,6 +1016,11 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
),
|
),
|
||||||
viewModel.stateFlow.value,
|
viewModel.stateFlow.value,
|
||||||
)
|
)
|
||||||
|
coVerify {
|
||||||
|
vaultRepository.getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList = listOf(cipherView1, cipherView2),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
|
@ -995,13 +1029,18 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
runTest {
|
runTest {
|
||||||
setupMockUri()
|
setupMockUri()
|
||||||
|
|
||||||
|
val cipherView1 = createMockCipherView(number = 1)
|
||||||
|
val cipherView2 = createMockCipherView(number = 2)
|
||||||
|
|
||||||
|
coEvery {
|
||||||
|
vaultRepository.getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList = listOf(cipherView1, cipherView2),
|
||||||
|
)
|
||||||
|
} returns DecryptFido2CredentialAutofillViewResult.Success(emptyList())
|
||||||
coEvery {
|
coEvery {
|
||||||
fido2CredentialManager.validateOrigin(any())
|
fido2CredentialManager.validateOrigin(any())
|
||||||
} returns Fido2ValidateOriginResult.Success
|
} returns Fido2ValidateOriginResult.Success
|
||||||
|
|
||||||
val cipherView1 = createMockCipherView(number = 1)
|
|
||||||
val cipherView2 = createMockCipherView(number = 2)
|
|
||||||
|
|
||||||
mockFilteredCiphers = listOf(cipherView1)
|
mockFilteredCiphers = listOf(cipherView1)
|
||||||
|
|
||||||
val fido2CredentialRequest = Fido2CredentialRequest(
|
val fido2CredentialRequest = Fido2CredentialRequest(
|
||||||
|
@ -1035,7 +1074,15 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
displayCollectionList = emptyList(),
|
displayCollectionList = emptyList(),
|
||||||
displayItemList = listOf(
|
displayItemList = listOf(
|
||||||
createMockDisplayItemForCipher(number = 1)
|
createMockDisplayItemForCipher(number = 1)
|
||||||
.copy(isFido2Creation = true),
|
.copy(
|
||||||
|
secondSubtitleTestTag = "PasskeySite",
|
||||||
|
subtitleTestTag = "PasskeyName",
|
||||||
|
iconData = IconData.Network(
|
||||||
|
uri = "https://vault.bitwarden.com/icons/www.mockuri.com/icon.png",
|
||||||
|
fallbackIconRes = R.drawable.ic_login_item_passkey,
|
||||||
|
),
|
||||||
|
isFido2Creation = true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
displayFolderList = emptyList(),
|
displayFolderList = emptyList(),
|
||||||
),
|
),
|
||||||
|
@ -1046,6 +1093,12 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
),
|
),
|
||||||
viewModel.stateFlow.value,
|
viewModel.stateFlow.value,
|
||||||
)
|
)
|
||||||
|
coVerify {
|
||||||
|
vaultRepository.getDecryptedFido2CredentialAutofillViews(
|
||||||
|
cipherViewList = listOf(cipherView1, cipherView2),
|
||||||
|
)
|
||||||
|
fido2CredentialManager.validateOrigin(any())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1139,7 +1192,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
viewState = VaultItemListingState.ViewState.Content(
|
viewState = VaultItemListingState.ViewState.Content(
|
||||||
displayCollectionList = emptyList(),
|
displayCollectionList = emptyList(),
|
||||||
displayItemList = listOf(
|
displayItemList = listOf(
|
||||||
createMockDisplayItemForCipher(number = 1),
|
createMockDisplayItemForCipher(number = 1)
|
||||||
|
.copy(secondSubtitleTestTag = "PasskeySite"),
|
||||||
),
|
),
|
||||||
displayFolderList = emptyList(),
|
displayFolderList = emptyList(),
|
||||||
),
|
),
|
||||||
|
@ -1249,7 +1303,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
viewState = VaultItemListingState.ViewState.Content(
|
viewState = VaultItemListingState.ViewState.Content(
|
||||||
displayCollectionList = emptyList(),
|
displayCollectionList = emptyList(),
|
||||||
displayItemList = listOf(
|
displayItemList = listOf(
|
||||||
createMockDisplayItemForCipher(number = 1),
|
createMockDisplayItemForCipher(number = 1)
|
||||||
|
.copy(secondSubtitleTestTag = "PasskeySite"),
|
||||||
),
|
),
|
||||||
displayFolderList = emptyList(),
|
displayFolderList = emptyList(),
|
||||||
),
|
),
|
||||||
|
@ -1369,7 +1424,8 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
|
||||||
viewState = VaultItemListingState.ViewState.Content(
|
viewState = VaultItemListingState.ViewState.Content(
|
||||||
displayCollectionList = emptyList(),
|
displayCollectionList = emptyList(),
|
||||||
displayItemList = listOf(
|
displayItemList = listOf(
|
||||||
createMockDisplayItemForCipher(number = 1),
|
createMockDisplayItemForCipher(number = 1)
|
||||||
|
.copy(secondSubtitleTestTag = "PasskeySite"),
|
||||||
),
|
),
|
||||||
displayFolderList = emptyList(),
|
displayFolderList = emptyList(),
|
||||||
),
|
),
|
||||||
|
|
|
@ -16,10 +16,12 @@ import com.x8bit.bitwarden.data.platform.repository.util.baseWebSendUrl
|
||||||
import com.x8bit.bitwarden.data.platform.util.subtitle
|
import com.x8bit.bitwarden.data.platform.util.subtitle
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
|
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionView
|
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionView
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFido2CredentialAutofillView
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
|
||||||
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
|
||||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||||
|
import com.x8bit.bitwarden.ui.platform.components.model.IconData
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.VaultItemListingState
|
import com.x8bit.bitwarden.ui.vault.feature.itemlisting.VaultItemListingState
|
||||||
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType
|
import com.x8bit.bitwarden.ui.vault.feature.vault.model.VaultFilterType
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
@ -397,6 +399,7 @@ class VaultItemListingDataExtensionsTest {
|
||||||
autofillSelectionData = null,
|
autofillSelectionData = null,
|
||||||
fido2CreationData = null,
|
fido2CreationData = null,
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
|
fido2CredentialAutofillViews = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
@ -408,22 +411,28 @@ class VaultItemListingDataExtensionsTest {
|
||||||
cipherType = CipherType.LOGIN,
|
cipherType = CipherType.LOGIN,
|
||||||
subtitle = null,
|
subtitle = null,
|
||||||
)
|
)
|
||||||
.copy(shouldShowMasterPasswordReprompt = true),
|
.copy(
|
||||||
|
secondSubtitleTestTag = "PasskeySite",
|
||||||
|
shouldShowMasterPasswordReprompt = true,
|
||||||
|
),
|
||||||
createMockDisplayItemForCipher(
|
createMockDisplayItemForCipher(
|
||||||
number = 2,
|
number = 2,
|
||||||
cipherType = CipherType.CARD,
|
cipherType = CipherType.CARD,
|
||||||
subtitle = null,
|
subtitle = null,
|
||||||
),
|
)
|
||||||
|
.copy(secondSubtitleTestTag = "PasskeySite"),
|
||||||
createMockDisplayItemForCipher(
|
createMockDisplayItemForCipher(
|
||||||
number = 3,
|
number = 3,
|
||||||
cipherType = CipherType.SECURE_NOTE,
|
cipherType = CipherType.SECURE_NOTE,
|
||||||
subtitle = null,
|
subtitle = null,
|
||||||
),
|
)
|
||||||
|
.copy(secondSubtitleTestTag = "PasskeySite"),
|
||||||
createMockDisplayItemForCipher(
|
createMockDisplayItemForCipher(
|
||||||
number = 4,
|
number = 4,
|
||||||
cipherType = CipherType.IDENTITY,
|
cipherType = CipherType.IDENTITY,
|
||||||
subtitle = null,
|
subtitle = null,
|
||||||
),
|
)
|
||||||
|
.copy(secondSubtitleTestTag = "PasskeySite"),
|
||||||
),
|
),
|
||||||
displayFolderList = emptyList(),
|
displayFolderList = emptyList(),
|
||||||
),
|
),
|
||||||
|
@ -455,6 +464,12 @@ class VaultItemListingDataExtensionsTest {
|
||||||
folderId = "mockId-1",
|
folderId = "mockId-1",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
val fido2CredentialAutofillViews = listOf(
|
||||||
|
createMockFido2CredentialAutofillView(
|
||||||
|
cipherId = "mockId-1",
|
||||||
|
number = 1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
val result = VaultData(
|
val result = VaultData(
|
||||||
cipherViewList = cipherViewList,
|
cipherViewList = cipherViewList,
|
||||||
|
@ -472,6 +487,7 @@ class VaultItemListingDataExtensionsTest {
|
||||||
),
|
),
|
||||||
fido2CreationData = null,
|
fido2CreationData = null,
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
|
fido2CredentialAutofillViews = fido2CredentialAutofillViews,
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
@ -484,6 +500,13 @@ class VaultItemListingDataExtensionsTest {
|
||||||
subtitle = null,
|
subtitle = null,
|
||||||
)
|
)
|
||||||
.copy(
|
.copy(
|
||||||
|
secondSubtitle = "mockRpId-1",
|
||||||
|
secondSubtitleTestTag = "PasskeySite",
|
||||||
|
subtitleTestTag = "PasskeyName",
|
||||||
|
iconData = IconData.Network(
|
||||||
|
uri = "https://vault.bitwarden.com/icons/www.mockuri.com/icon.png",
|
||||||
|
fallbackIconRes = R.drawable.ic_login_item_passkey,
|
||||||
|
),
|
||||||
isAutofill = true,
|
isAutofill = true,
|
||||||
shouldShowMasterPasswordReprompt = true,
|
shouldShowMasterPasswordReprompt = true,
|
||||||
),
|
),
|
||||||
|
@ -492,7 +515,11 @@ class VaultItemListingDataExtensionsTest {
|
||||||
cipherType = CipherType.CARD,
|
cipherType = CipherType.CARD,
|
||||||
subtitle = null,
|
subtitle = null,
|
||||||
)
|
)
|
||||||
.copy(isAutofill = true),
|
.copy(
|
||||||
|
secondSubtitleTestTag = "PasskeySite",
|
||||||
|
subtitleTestTag = "PasswordName",
|
||||||
|
isAutofill = true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
displayFolderList = emptyList(),
|
displayFolderList = emptyList(),
|
||||||
),
|
),
|
||||||
|
@ -525,6 +552,7 @@ class VaultItemListingDataExtensionsTest {
|
||||||
autofillSelectionData = null,
|
autofillSelectionData = null,
|
||||||
fido2CreationData = null,
|
fido2CreationData = null,
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
|
fido2CredentialAutofillViews = null,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -545,6 +573,7 @@ class VaultItemListingDataExtensionsTest {
|
||||||
autofillSelectionData = null,
|
autofillSelectionData = null,
|
||||||
fido2CreationData = null,
|
fido2CreationData = null,
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
|
fido2CredentialAutofillViews = null,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -563,6 +592,7 @@ class VaultItemListingDataExtensionsTest {
|
||||||
autofillSelectionData = null,
|
autofillSelectionData = null,
|
||||||
fido2CreationData = null,
|
fido2CreationData = null,
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
|
fido2CredentialAutofillViews = null,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -584,6 +614,7 @@ class VaultItemListingDataExtensionsTest {
|
||||||
),
|
),
|
||||||
fido2CreationData = null,
|
fido2CreationData = null,
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
|
fido2CredentialAutofillViews = null,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -608,6 +639,7 @@ class VaultItemListingDataExtensionsTest {
|
||||||
origin = "https://www.test.com",
|
origin = "https://www.test.com",
|
||||||
),
|
),
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
|
fido2CredentialAutofillViews = null,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -748,6 +780,7 @@ class VaultItemListingDataExtensionsTest {
|
||||||
vaultFilterType = VaultFilterType.AllVaults,
|
vaultFilterType = VaultFilterType.AllVaults,
|
||||||
fido2CreationData = null,
|
fido2CreationData = null,
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
|
fido2CredentialAutofillViews = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
@ -789,6 +822,7 @@ class VaultItemListingDataExtensionsTest {
|
||||||
vaultFilterType = VaultFilterType.AllVaults,
|
vaultFilterType = VaultFilterType.AllVaults,
|
||||||
fido2CreationData = null,
|
fido2CreationData = null,
|
||||||
hasMasterPassword = true,
|
hasMasterPassword = true,
|
||||||
|
fido2CredentialAutofillViews = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
|
|
@ -23,6 +23,8 @@ fun createMockDisplayItemForCipher(
|
||||||
id = "mockId-$number",
|
id = "mockId-$number",
|
||||||
title = "mockName-$number",
|
title = "mockName-$number",
|
||||||
titleTestTag = "CipherNameLabel",
|
titleTestTag = "CipherNameLabel",
|
||||||
|
secondSubtitle = null,
|
||||||
|
secondSubtitleTestTag = null,
|
||||||
subtitle = subtitle,
|
subtitle = subtitle,
|
||||||
subtitleTestTag = "CipherSubTitleLabel",
|
subtitleTestTag = "CipherSubTitleLabel",
|
||||||
iconData = IconData.Network(
|
iconData = IconData.Network(
|
||||||
|
@ -75,6 +77,8 @@ fun createMockDisplayItemForCipher(
|
||||||
id = "mockId-$number",
|
id = "mockId-$number",
|
||||||
title = "mockName-$number",
|
title = "mockName-$number",
|
||||||
titleTestTag = "CipherNameLabel",
|
titleTestTag = "CipherNameLabel",
|
||||||
|
secondSubtitle = null,
|
||||||
|
secondSubtitleTestTag = null,
|
||||||
subtitle = subtitle,
|
subtitle = subtitle,
|
||||||
subtitleTestTag = "CipherSubTitleLabel",
|
subtitleTestTag = "CipherSubTitleLabel",
|
||||||
iconData = IconData.Local(R.drawable.ic_secure_note_item),
|
iconData = IconData.Local(R.drawable.ic_secure_note_item),
|
||||||
|
@ -113,6 +117,8 @@ fun createMockDisplayItemForCipher(
|
||||||
id = "mockId-$number",
|
id = "mockId-$number",
|
||||||
title = "mockName-$number",
|
title = "mockName-$number",
|
||||||
titleTestTag = "CipherNameLabel",
|
titleTestTag = "CipherNameLabel",
|
||||||
|
secondSubtitle = null,
|
||||||
|
secondSubtitleTestTag = null,
|
||||||
subtitle = subtitle,
|
subtitle = subtitle,
|
||||||
subtitleTestTag = "CipherSubTitleLabel",
|
subtitleTestTag = "CipherSubTitleLabel",
|
||||||
iconData = IconData.Local(R.drawable.ic_card_item),
|
iconData = IconData.Local(R.drawable.ic_card_item),
|
||||||
|
@ -157,6 +163,8 @@ fun createMockDisplayItemForCipher(
|
||||||
id = "mockId-$number",
|
id = "mockId-$number",
|
||||||
title = "mockName-$number",
|
title = "mockName-$number",
|
||||||
titleTestTag = "CipherNameLabel",
|
titleTestTag = "CipherNameLabel",
|
||||||
|
secondSubtitle = null,
|
||||||
|
secondSubtitleTestTag = null,
|
||||||
subtitle = subtitle,
|
subtitle = subtitle,
|
||||||
subtitleTestTag = "CipherSubTitleLabel",
|
subtitleTestTag = "CipherSubTitleLabel",
|
||||||
iconData = IconData.Local(R.drawable.ic_identity_item),
|
iconData = IconData.Local(R.drawable.ic_identity_item),
|
||||||
|
@ -202,6 +210,8 @@ fun createMockDisplayItemForSend(
|
||||||
id = "mockId-$number",
|
id = "mockId-$number",
|
||||||
title = "mockName-$number",
|
title = "mockName-$number",
|
||||||
titleTestTag = "SendNameLabel",
|
titleTestTag = "SendNameLabel",
|
||||||
|
secondSubtitle = null,
|
||||||
|
secondSubtitleTestTag = null,
|
||||||
subtitle = "Oct 27, 2023, 12:00 PM",
|
subtitle = "Oct 27, 2023, 12:00 PM",
|
||||||
subtitleTestTag = "SendDateLabel",
|
subtitleTestTag = "SendDateLabel",
|
||||||
iconData = IconData.Local(R.drawable.ic_send_file),
|
iconData = IconData.Local(R.drawable.ic_send_file),
|
||||||
|
@ -241,6 +251,8 @@ fun createMockDisplayItemForSend(
|
||||||
id = "mockId-$number",
|
id = "mockId-$number",
|
||||||
title = "mockName-$number",
|
title = "mockName-$number",
|
||||||
titleTestTag = "SendNameLabel",
|
titleTestTag = "SendNameLabel",
|
||||||
|
secondSubtitle = null,
|
||||||
|
secondSubtitleTestTag = null,
|
||||||
subtitle = "Oct 27, 2023, 12:00 PM",
|
subtitle = "Oct 27, 2023, 12:00 PM",
|
||||||
subtitleTestTag = "SendDateLabel",
|
subtitleTestTag = "SendDateLabel",
|
||||||
iconData = IconData.Local(R.drawable.ic_send_text),
|
iconData = IconData.Local(R.drawable.ic_send_text),
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.time.Clock
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
|
|
||||||
|
@Suppress("LargeClass")
|
||||||
class VaultDataExtensionsTest {
|
class VaultDataExtensionsTest {
|
||||||
|
|
||||||
private val clock: Clock = Clock.fixed(
|
private val clock: Clock = Clock.fixed(
|
||||||
|
@ -352,7 +353,6 @@ class VaultDataExtensionsTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
|
||||||
@Test
|
@Test
|
||||||
fun `toViewState should omit non org related totp codes when user does not have premium`() {
|
fun `toViewState should omit non org related totp codes when user does not have premium`() {
|
||||||
val vaultData = VaultData(
|
val vaultData = VaultData(
|
||||||
|
@ -390,7 +390,6 @@ class VaultDataExtensionsTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
|
||||||
@Test
|
@Test
|
||||||
fun `toLoginIconData should return a IconData Local type if isIconLoadingDisabled is true`() {
|
fun `toLoginIconData should return a IconData Local type if isIconLoadingDisabled is true`() {
|
||||||
val actual =
|
val actual =
|
||||||
|
@ -403,6 +402,7 @@ class VaultDataExtensionsTest {
|
||||||
.toLoginIconData(
|
.toLoginIconData(
|
||||||
isIconLoadingDisabled = true,
|
isIconLoadingDisabled = true,
|
||||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
val expected = IconData.Local(iconRes = R.drawable.ic_login_item)
|
val expected = IconData.Local(iconRes = R.drawable.ic_login_item)
|
||||||
|
@ -411,6 +411,26 @@ class VaultDataExtensionsTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `toLoginIconData should return a IconData Local type if isIconLoadingDisabled is true and usePasskeyDefaultIcon true`() {
|
||||||
|
val actual =
|
||||||
|
createMockCipherView(
|
||||||
|
number = 1,
|
||||||
|
cipherType = CipherType.LOGIN,
|
||||||
|
)
|
||||||
|
.login
|
||||||
|
?.uris
|
||||||
|
.toLoginIconData(
|
||||||
|
isIconLoadingDisabled = true,
|
||||||
|
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
val expected = IconData.Local(iconRes = R.drawable.ic_login_item_passkey)
|
||||||
|
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `toLoginIconData should return a IconData Local type if no valid uris are found`() {
|
fun `toLoginIconData should return a IconData Local type if no valid uris are found`() {
|
||||||
val actual = listOf(
|
val actual = listOf(
|
||||||
|
@ -423,6 +443,7 @@ class VaultDataExtensionsTest {
|
||||||
.toLoginIconData(
|
.toLoginIconData(
|
||||||
isIconLoadingDisabled = false,
|
isIconLoadingDisabled = false,
|
||||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
val expected = IconData.Local(iconRes = R.drawable.ic_login_item)
|
val expected = IconData.Local(iconRes = R.drawable.ic_login_item)
|
||||||
|
@ -431,6 +452,26 @@ class VaultDataExtensionsTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `toLoginIconData should return a IconData Local type if no valid uris are found and usePasskeyDefaultIcon true`() {
|
||||||
|
val actual = listOf(
|
||||||
|
LoginUriView(
|
||||||
|
uri = "",
|
||||||
|
match = UriMatchType.HOST,
|
||||||
|
uriChecksum = null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toLoginIconData(
|
||||||
|
isIconLoadingDisabled = false,
|
||||||
|
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
val expected = IconData.Local(iconRes = R.drawable.ic_login_item_passkey)
|
||||||
|
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `toLoginIconData should return a IconData Local type if an Android uri is detected`() {
|
fun `toLoginIconData should return a IconData Local type if an Android uri is detected`() {
|
||||||
val actual = listOf(
|
val actual = listOf(
|
||||||
|
@ -443,6 +484,7 @@ class VaultDataExtensionsTest {
|
||||||
.toLoginIconData(
|
.toLoginIconData(
|
||||||
isIconLoadingDisabled = false,
|
isIconLoadingDisabled = false,
|
||||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
val expected = IconData.Local(iconRes = R.drawable.ic_android)
|
val expected = IconData.Local(iconRes = R.drawable.ic_android)
|
||||||
|
@ -450,7 +492,6 @@ class VaultDataExtensionsTest {
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
|
||||||
@Test
|
@Test
|
||||||
fun `toLoginIconData should return a IconData Local type if an iOS uri is detected`() {
|
fun `toLoginIconData should return a IconData Local type if an iOS uri is detected`() {
|
||||||
val actual = listOf(
|
val actual = listOf(
|
||||||
|
@ -463,6 +504,7 @@ class VaultDataExtensionsTest {
|
||||||
.toLoginIconData(
|
.toLoginIconData(
|
||||||
isIconLoadingDisabled = false,
|
isIconLoadingDisabled = false,
|
||||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
val expected = IconData.Local(iconRes = R.drawable.ic_ios)
|
val expected = IconData.Local(iconRes = R.drawable.ic_ios)
|
||||||
|
@ -470,7 +512,6 @@ class VaultDataExtensionsTest {
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
|
||||||
@Test
|
@Test
|
||||||
fun `toLoginIconData should return IconData Network type if isIconLoadingDisabled is false`() {
|
fun `toLoginIconData should return IconData Network type if isIconLoadingDisabled is false`() {
|
||||||
mockkStatic(Uri::class)
|
mockkStatic(Uri::class)
|
||||||
|
@ -488,6 +529,7 @@ class VaultDataExtensionsTest {
|
||||||
.toLoginIconData(
|
.toLoginIconData(
|
||||||
isIconLoadingDisabled = false,
|
isIconLoadingDisabled = false,
|
||||||
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = false,
|
||||||
)
|
)
|
||||||
|
|
||||||
val expected = IconData.Network(
|
val expected = IconData.Network(
|
||||||
|
@ -501,6 +543,36 @@ class VaultDataExtensionsTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `toLoginIconData should return IconData Network type if isIconLoadingDisabled is false and usePasskeyDefaultIcon`() {
|
||||||
|
mockkStatic(Uri::class)
|
||||||
|
val uriMock = mockk<Uri>()
|
||||||
|
every { Uri.parse(any()) } returns uriMock
|
||||||
|
every { uriMock.host } returns "www.mockuri1.com"
|
||||||
|
|
||||||
|
val actual =
|
||||||
|
createMockCipherView(
|
||||||
|
number = 1,
|
||||||
|
cipherType = CipherType.LOGIN,
|
||||||
|
)
|
||||||
|
.login
|
||||||
|
?.uris
|
||||||
|
.toLoginIconData(
|
||||||
|
isIconLoadingDisabled = false,
|
||||||
|
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
val expected = IconData.Network(
|
||||||
|
uri = "https://vault.bitwarden.com/icons/www.mockuri1.com/icon.png",
|
||||||
|
fallbackIconRes = R.drawable.ic_login_item_passkey,
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
|
||||||
|
unmockkStatic(Uri::class)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `toViewState should only count deleted items for the trash count`() {
|
fun `toViewState should only count deleted items for the trash count`() {
|
||||||
val vaultData = VaultData(
|
val vaultData = VaultData(
|
||||||
|
@ -539,7 +611,6 @@ class VaultDataExtensionsTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
|
||||||
@Test
|
@Test
|
||||||
fun `toViewState with over 100 no folder items should show no folder option`() {
|
fun `toViewState with over 100 no folder items should show no folder option`() {
|
||||||
mockkStatic(Uri::class)
|
mockkStatic(Uri::class)
|
||||||
|
|
|
@ -546,6 +546,7 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
|
||||||
startIcon = cipherView.login?.uris.toLoginIconData(
|
startIcon = cipherView.login?.uris.toLoginIconData(
|
||||||
isIconLoadingDisabled = initialState.isIconLoadingDisabled,
|
isIconLoadingDisabled = initialState.isIconLoadingDisabled,
|
||||||
baseIconUrl = initialState.baseIconUrl,
|
baseIconUrl = initialState.baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = false,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -566,6 +567,7 @@ class VerificationCodeViewModelTest : BaseViewModelTest() {
|
||||||
startIcon = cipherView.login?.uris.toLoginIconData(
|
startIcon = cipherView.login?.uris.toLoginIconData(
|
||||||
isIconLoadingDisabled = initialState.isIconLoadingDisabled,
|
isIconLoadingDisabled = initialState.isIconLoadingDisabled,
|
||||||
baseIconUrl = initialState.baseIconUrl,
|
baseIconUrl = initialState.baseIconUrl,
|
||||||
|
usePasskeyDefaultIcon = false,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue