BIT-2291: Bypass master password reprompt when user has no password (#1301)

This commit is contained in:
David Perez 2024-04-24 10:17:29 -05:00 committed by Álison Fernandes
parent b730330196
commit 0a03df75cc
25 changed files with 373 additions and 73 deletions

View file

@ -65,7 +65,12 @@ data class UserState(
val organizations: List<Organization>,
val isBiometricsEnabled: Boolean,
val vaultUnlockType: VaultUnlockType = VaultUnlockType.MASTER_PASSWORD,
)
) {
/**
* Indicates that the user does or does not have a master password.
*/
val hasMasterPassword: Boolean get() = trustedDevice?.hasMasterPassword != false
}
/**
* Models the data related to trusted device encryption (TDE).

View file

@ -97,6 +97,7 @@ class SearchViewModel @Inject constructor(
baseIconUrl = environmentRepo.environment.environmentUrlData.baseIconUrl,
isIconLoadingDisabled = settingsRepo.isIconLoadingDisabled,
autofillSelectionData = autofillSelectionData,
hasMasterPassword = userState.activeAccount.hasMasterPassword,
)
},
) {
@ -640,6 +641,7 @@ class SearchViewModel @Inject constructor(
)
.toViewState(
searchTerm = state.searchTerm,
hasMasterPassword = state.hasMasterPassword,
baseIconUrl = state.baseIconUrl,
isIconLoadingDisabled = state.isIconLoadingDisabled,
isAutofill = state.isAutofill,
@ -686,6 +688,7 @@ data class SearchState(
val isIconLoadingDisabled: Boolean,
// Internal
val autofillSelectionData: AutofillSelectionData? = null,
val hasMasterPassword: Boolean,
) : Parcelable {
/**

View file

@ -140,6 +140,7 @@ private fun CipherView.matchedSearch(searchTerm: String): SortPriority? {
fun List<CipherView>.toViewState(
searchTerm: String,
baseIconUrl: String,
hasMasterPassword: Boolean,
isIconLoadingDisabled: Boolean,
isAutofill: Boolean,
): SearchState.ViewState =
@ -149,6 +150,7 @@ fun List<CipherView>.toViewState(
SearchState.ViewState.Content(
displayItems = toDisplayItemList(
baseIconUrl = baseIconUrl,
hasMasterPassword = hasMasterPassword,
isIconLoadingDisabled = isIconLoadingDisabled,
isAutofill = isAutofill,
),
@ -164,12 +166,14 @@ fun List<CipherView>.toViewState(
private fun List<CipherView>.toDisplayItemList(
baseIconUrl: String,
hasMasterPassword: Boolean,
isIconLoadingDisabled: Boolean,
isAutofill: Boolean,
): List<SearchState.DisplayItem> =
this.map {
it.toDisplayItem(
baseIconUrl = baseIconUrl,
hasMasterPassword = hasMasterPassword,
isIconLoadingDisabled = isIconLoadingDisabled,
isAutofill = isAutofill,
)
@ -177,6 +181,7 @@ private fun List<CipherView>.toDisplayItemList(
private fun CipherView.toDisplayItem(
baseIconUrl: String,
hasMasterPassword: Boolean,
isIconLoadingDisabled: Boolean,
isAutofill: Boolean,
): SearchState.DisplayItem =
@ -191,7 +196,7 @@ private fun CipherView.toDisplayItem(
isIconLoadingDisabled = isIconLoadingDisabled,
),
extraIconList = toLabelIcons(),
overflowOptions = toOverflowActions(),
overflowOptions = toOverflowActions(hasMasterPassword = hasMasterPassword),
overflowTestTag = "CipherOptionsButton",
totpCode = login?.totp,
autofillSelectionOptions = AutofillSelectionOption

View file

@ -749,8 +749,7 @@ class VaultItemViewModel @Inject constructor(
mutableStateFlow.update {
it.copy(
viewState = vaultDataState.toViewStateOrError(
isPremiumUser = userState.activeAccount.isPremium,
totpCodeItemData = vaultDataState.data?.totpCodeItemData,
account = userState.activeAccount,
errorText = R.string.generic_error_message.asText(),
),
)
@ -761,8 +760,7 @@ class VaultItemViewModel @Inject constructor(
mutableStateFlow.update {
it.copy(
viewState = vaultDataState.toViewStateOrError(
isPremiumUser = userState.activeAccount.isPremium,
totpCodeItemData = vaultDataState.data.totpCodeItemData,
account = userState.activeAccount,
errorText = R.string.generic_error_message.asText(),
),
)
@ -779,8 +777,7 @@ class VaultItemViewModel @Inject constructor(
mutableStateFlow.update {
it.copy(
viewState = vaultDataState.toViewStateOrError(
isPremiumUser = userState.activeAccount.isPremium,
totpCodeItemData = vaultDataState.data?.totpCodeItemData,
account = userState.activeAccount,
errorText = R.string.internet_connection_required_title
.asText()
.concat(
@ -796,8 +793,7 @@ class VaultItemViewModel @Inject constructor(
mutableStateFlow.update {
it.copy(
viewState = vaultDataState.toViewStateOrError(
isPremiumUser = userState.activeAccount.isPremium,
totpCodeItemData = vaultDataState.data.totpCodeItemData,
account = userState.activeAccount,
errorText = R.string.generic_error_message.asText(),
),
)
@ -807,13 +803,16 @@ class VaultItemViewModel @Inject constructor(
}
private fun DataState<VaultItemStateData>.toViewStateOrError(
isPremiumUser: Boolean,
totpCodeItemData: TotpCodeItemData?,
account: UserState.Account,
errorText: Text,
): VaultItemState.ViewState = this
.data
?.cipher
?.toViewState(isPremiumUser = isPremiumUser, totpCodeItemData = totpCodeItemData)
?.toViewState(
isPremiumUser = account.isPremium,
hasMasterPassword = account.hasMasterPassword,
totpCodeItemData = this.data?.totpCodeItemData,
)
?: VaultItemState.ViewState.Error(message = errorText)
private fun handleValidatePasswordReceive(

View file

@ -33,9 +33,10 @@ private const val FIDO2_CREDENTIAL_CREATION_TIME_PATTERN: String = "h:mm a"
/**
* Transforms [VaultData] into [VaultState.ViewState].
*/
@Suppress("LongMethod")
@Suppress("CyclomaticComplexMethod", "LongMethod")
fun CipherView.toViewState(
isPremiumUser: Boolean,
hasMasterPassword: Boolean,
totpCodeItemData: TotpCodeItemData?,
clock: Clock = Clock.systemDefaultZone(),
): VaultItemState.ViewState =
@ -43,7 +44,7 @@ fun CipherView.toViewState(
common = VaultItemState.ViewState.Content.Common(
currentCipher = this,
name = name,
requiresReprompt = reprompt == CipherRepromptType.PASSWORD,
requiresReprompt = reprompt == CipherRepromptType.PASSWORD && hasMasterPassword,
customFields = fields.orEmpty().map { it.toCustomField() },
lastUpdated = revisionDate.toFormattedPattern(
pattern = LAST_UPDATED_DATE_TIME_PATTERN,

View file

@ -99,6 +99,7 @@ class VaultItemListingViewModel @Inject constructor(
.any(),
autofillSelectionData = specialCircumstance?.autofillSelectionData,
shouldFinishOnComplete = specialCircumstance?.shouldFinishWhenComplete ?: false,
hasMasterPassword = userState.activeAccount.hasMasterPassword,
)
},
) {
@ -686,6 +687,7 @@ class VaultItemListingViewModel @Inject constructor(
vaultData.toViewState(
vaultFilterType = state.vaultFilterType,
itemListingType = listingType,
hasMasterPassword = state.hasMasterPassword,
baseIconUrl = state.baseIconUrl,
isIconLoadingDisabled = state.isIconLoadingDisabled,
autofillSelectionData = state.autofillSelectionData,
@ -754,6 +756,7 @@ data class VaultItemListingState(
private val isPullToRefreshSettingEnabled: Boolean,
val autofillSelectionData: AutofillSelectionData? = null,
val shouldFinishOnComplete: Boolean = false,
val hasMasterPassword: Boolean,
) {
/**
* Whether or not this represents a listing screen for autofill.

View file

@ -84,9 +84,11 @@ sealed class ListingItemOverflowAction : Parcelable {
* Click on the edit cipher overflow option.
*/
@Parcelize
data class EditClick(val cipherId: String) : VaultAction() {
data class EditClick(
val cipherId: String,
override val requiresPasswordReprompt: Boolean,
) : VaultAction() {
override val title: Text get() = R.string.edit.asText()
override val requiresPasswordReprompt: Boolean get() = true
}
/**
@ -102,9 +104,11 @@ sealed class ListingItemOverflowAction : Parcelable {
* Click on the copy password overflow option.
*/
@Parcelize
data class CopyPasswordClick(val password: String) : VaultAction() {
data class CopyPasswordClick(
val password: String,
override val requiresPasswordReprompt: Boolean,
) : VaultAction() {
override val title: Text get() = R.string.copy_password.asText()
override val requiresPasswordReprompt: Boolean get() = true
}
/**
@ -120,18 +124,22 @@ sealed class ListingItemOverflowAction : Parcelable {
* Click on the copy number overflow option.
*/
@Parcelize
data class CopyNumberClick(val number: String) : VaultAction() {
data class CopyNumberClick(
val number: String,
override val requiresPasswordReprompt: Boolean,
) : VaultAction() {
override val title: Text get() = R.string.copy_number.asText()
override val requiresPasswordReprompt: Boolean get() = true
}
/**
* Click on the copy security code overflow option.
*/
@Parcelize
data class CopySecurityCodeClick(val securityCode: String) : VaultAction() {
data class CopySecurityCodeClick(
val securityCode: String,
override val requiresPasswordReprompt: Boolean,
) : VaultAction() {
override val title: Text get() = R.string.copy_security_code.asText()
override val requiresPasswordReprompt: Boolean get() = true
}
/**

View file

@ -91,10 +91,11 @@ fun SendView.determineListingPredicate(
/**
* Transforms a list of [CipherView] into [VaultItemListingState.ViewState].
*/
@Suppress("CyclomaticComplexMethod", "LongMethod")
@Suppress("CyclomaticComplexMethod", "LongMethod", "LongParameterList")
fun VaultData.toViewState(
itemListingType: VaultItemListingState.ItemListingType.Vault,
vaultFilterType: VaultFilterType,
hasMasterPassword: Boolean,
baseIconUrl: String,
isIconLoadingDisabled: Boolean,
autofillSelectionData: AutofillSelectionData?,
@ -122,6 +123,7 @@ fun VaultData.toViewState(
VaultItemListingState.ViewState.Content(
displayItemList = filteredCipherViewList.toDisplayItemList(
baseIconUrl = baseIconUrl,
hasMasterPassword = hasMasterPassword,
isIconLoadingDisabled = isIconLoadingDisabled,
isAutofill = autofillSelectionData != null,
),
@ -246,12 +248,14 @@ fun VaultItemListingState.ItemListingType.updateWithAdditionalDataIfNecessary(
private fun List<CipherView>.toDisplayItemList(
baseIconUrl: String,
hasMasterPassword: Boolean,
isIconLoadingDisabled: Boolean,
isAutofill: Boolean,
): List<VaultItemListingState.DisplayItem> =
this.map {
it.toDisplayItem(
baseIconUrl = baseIconUrl,
hasMasterPassword = hasMasterPassword,
isIconLoadingDisabled = isIconLoadingDisabled,
isAutofill = isAutofill,
)
@ -270,6 +274,7 @@ private fun List<SendView>.toDisplayItemList(
private fun CipherView.toDisplayItem(
baseIconUrl: String,
hasMasterPassword: Boolean,
isIconLoadingDisabled: Boolean,
isAutofill: Boolean,
): VaultItemListingState.DisplayItem =
@ -285,7 +290,7 @@ private fun CipherView.toDisplayItem(
),
iconTestTag = toIconTestTag(),
extraIconList = toLabelIcons(),
overflowOptions = toOverflowActions(),
overflowOptions = toOverflowActions(hasMasterPassword = hasMasterPassword),
optionsTestTag = "CipherOptionsButton",
isAutofill = isAutofill,
shouldShowMasterPasswordReprompt = reprompt == CipherRepromptType.PASSWORD,

View file

@ -9,28 +9,44 @@ import com.x8bit.bitwarden.ui.vault.model.VaultTrailingIcon
/**
* Creates the list of overflow actions to be displayed for a [CipherView].
*/
fun CipherView.toOverflowActions(): List<ListingItemOverflowAction.VaultAction> =
fun CipherView.toOverflowActions(
hasMasterPassword: Boolean,
): List<ListingItemOverflowAction.VaultAction> =
this
.id
?.let { cipherId ->
listOfNotNull(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = cipherId),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = cipherId)
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = cipherId,
requiresPasswordReprompt = hasMasterPassword,
)
.takeUnless { this.deletedDate != null },
this.login?.username?.let {
ListingItemOverflowAction.VaultAction.CopyUsernameClick(username = it)
},
this.login?.password
?.let { ListingItemOverflowAction.VaultAction.CopyPasswordClick(password = it) }
?.let {
ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = it,
requiresPasswordReprompt = hasMasterPassword,
)
}
.takeIf { this.viewPassword },
this.login?.totp
?.let { ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode = it) }
.takeIf { this.type == CipherType.LOGIN },
this.card?.number?.let {
ListingItemOverflowAction.VaultAction.CopyNumberClick(number = it)
ListingItemOverflowAction.VaultAction.CopyNumberClick(
number = it,
requiresPasswordReprompt = hasMasterPassword,
)
},
this.card?.code?.let {
ListingItemOverflowAction.VaultAction.CopySecurityCodeClick(securityCode = it)
ListingItemOverflowAction.VaultAction.CopySecurityCodeClick(
securityCode = it,
requiresPasswordReprompt = hasMasterPassword,
)
},
this.notes
?.let { ListingItemOverflowAction.VaultAction.CopyNoteClick(notes = it) }

View file

@ -83,6 +83,7 @@ class VaultViewModel @Inject constructor(
isPremium = userState.activeAccount.isPremium,
isPullToRefreshSettingEnabled = settingsRepository.getPullToRefreshEnabledFlow().value,
baseIconUrl = userState.activeAccount.environment.environmentUrlData.baseIconUrl,
hasMasterPassword = userState.activeAccount.hasMasterPassword,
)
},
) {
@ -493,6 +494,7 @@ class VaultViewModel @Inject constructor(
vaultFilterType = vaultFilterTypeOrDefault,
isIconLoadingDisabled = state.isIconLoadingDisabled,
isPremium = state.isPremium,
hasMasterPassword = state.hasMasterPassword,
errorTitle = R.string.an_error_has_occurred.asText(),
errorMessage = R.string.generic_error_message.asText(),
)
@ -513,6 +515,7 @@ class VaultViewModel @Inject constructor(
baseIconUrl = state.baseIconUrl,
isIconLoadingDisabled = state.isIconLoadingDisabled,
isPremium = state.isPremium,
hasMasterPassword = state.hasMasterPassword,
vaultFilterType = vaultFilterTypeOrDefault,
),
dialog = null,
@ -533,6 +536,7 @@ class VaultViewModel @Inject constructor(
isPremium = state.isPremium,
errorTitle = R.string.internet_connection_required_title.asText(),
isIconLoadingDisabled = state.isIconLoadingDisabled,
hasMasterPassword = state.hasMasterPassword,
errorMessage = R.string.internet_connection_required_message.asText(),
)
sendEvent(VaultEvent.DismissPullToRefresh)
@ -545,6 +549,7 @@ class VaultViewModel @Inject constructor(
baseIconUrl = state.baseIconUrl,
isIconLoadingDisabled = state.isIconLoadingDisabled,
isPremium = state.isPremium,
hasMasterPassword = state.hasMasterPassword,
vaultFilterType = vaultFilterTypeOrDefault,
),
)
@ -610,6 +615,7 @@ data class VaultState(
// Internal-use properties
val isSwitchingAccounts: Boolean = false,
val isPremium: Boolean,
val hasMasterPassword: Boolean,
private val isPullToRefreshSettingEnabled: Boolean,
val baseIconUrl: String,
val isIconLoadingDisabled: Boolean,
@ -1163,6 +1169,7 @@ private fun MutableStateFlow<VaultState>.updateToErrorStateOrDialog(
vaultFilterType: VaultFilterType,
isIconLoadingDisabled: Boolean,
isPremium: Boolean,
hasMasterPassword: Boolean,
errorTitle: Text,
errorMessage: Text,
) {
@ -1172,6 +1179,7 @@ private fun MutableStateFlow<VaultState>.updateToErrorStateOrDialog(
viewState = vaultData.toViewState(
baseIconUrl = baseIconUrl,
isPremium = isPremium,
hasMasterPassword = hasMasterPassword,
vaultFilterType = vaultFilterType,
isIconLoadingDisabled = isIconLoadingDisabled,
),

View file

@ -35,6 +35,7 @@ private const val NO_FOLDER_ITEM_THRESHOLD: Int = 100
@Suppress("LongMethod")
fun VaultData.toViewState(
isPremium: Boolean,
hasMasterPassword: Boolean,
isIconLoadingDisabled: Boolean,
baseIconUrl: String,
vaultFilterType: VaultFilterType,
@ -78,6 +79,7 @@ fun VaultData.toViewState(
.filter { it.favorite }
.mapNotNull {
it.toVaultItemOrNull(
hasMasterPassword = hasMasterPassword,
isIconLoadingDisabled = isIconLoadingDisabled,
baseIconUrl = baseIconUrl,
)
@ -110,6 +112,7 @@ fun VaultData.toViewState(
noFolderItems = noFolderItems
.mapNotNull {
it.toVaultItemOrNull(
hasMasterPassword = hasMasterPassword,
isIconLoadingDisabled = isIconLoadingDisabled,
baseIconUrl = baseIconUrl,
)
@ -183,6 +186,7 @@ fun List<LoginUriView>?.toLoginIconData(
*/
@Suppress("MagicNumber")
private fun CipherView.toVaultItemOrNull(
hasMasterPassword: Boolean,
isIconLoadingDisabled: Boolean,
baseIconUrl: String,
): VaultState.ViewState.VaultItem? {
@ -196,7 +200,7 @@ private fun CipherView.toVaultItemOrNull(
isIconLoadingDisabled = isIconLoadingDisabled,
baseIconUrl = baseIconUrl,
),
overflowOptions = toOverflowActions(),
overflowOptions = toOverflowActions(hasMasterPassword = hasMasterPassword),
extraIconList = toLabelIcons(),
shouldShowMasterPasswordReprompt = reprompt == CipherRepromptType.PASSWORD,
)
@ -204,7 +208,7 @@ private fun CipherView.toVaultItemOrNull(
CipherType.SECURE_NOTE -> VaultState.ViewState.VaultItem.SecureNote(
id = id,
name = name.asText(),
overflowOptions = toOverflowActions(),
overflowOptions = toOverflowActions(hasMasterPassword = hasMasterPassword),
extraIconList = toLabelIcons(),
shouldShowMasterPasswordReprompt = reprompt == CipherRepromptType.PASSWORD,
)
@ -216,7 +220,7 @@ private fun CipherView.toVaultItemOrNull(
lastFourDigits = card?.number
?.takeLast(4)
?.asText(),
overflowOptions = toOverflowActions(),
overflowOptions = toOverflowActions(hasMasterPassword = hasMasterPassword),
extraIconList = toLabelIcons(),
shouldShowMasterPasswordReprompt = reprompt == CipherRepromptType.PASSWORD,
)
@ -230,7 +234,7 @@ private fun CipherView.toVaultItemOrNull(
else -> "${identity?.firstName} ${identity?.lastName}"
}
?.asText(),
overflowOptions = toOverflowActions(),
overflowOptions = toOverflowActions(hasMasterPassword = hasMasterPassword),
extraIconList = toLabelIcons(),
shouldShowMasterPasswordReprompt = reprompt == CipherRepromptType.PASSWORD,
)

View file

@ -541,6 +541,7 @@ class SearchScreenTest : BaseComposeTest() {
SearchAction.OverflowOptionClick(
overflowAction = ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-1",
requiresPasswordReprompt = true,
),
),
)
@ -579,6 +580,7 @@ class SearchScreenTest : BaseComposeTest() {
SearchAction.OverflowOptionClick(
overflowAction = ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = "mockPassword-1",
requiresPasswordReprompt = true,
),
),
)
@ -675,6 +677,7 @@ class SearchScreenTest : BaseComposeTest() {
masterPasswordRepromptData = MasterPasswordRepromptData.OverflowItem(
action = ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-1",
requiresPasswordReprompt = true,
),
),
),
@ -882,6 +885,7 @@ private val DEFAULT_STATE: SearchState = SearchState(
baseWebSendUrl = "www.test.com",
baseIconUrl = "www.test.com",
isIconLoadingDisabled = false,
hasMasterPassword = true,
)
private fun createStateForAutofill(

View file

@ -477,6 +477,7 @@ class SearchViewModelTest : BaseViewModelTest() {
masterPasswordRepromptData = MasterPasswordRepromptData.OverflowItem(
action = ListingItemOverflowAction.VaultAction.EditClick(
cipherId = cipherId,
requiresPasswordReprompt = true,
),
),
),
@ -665,7 +666,10 @@ class SearchViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
viewModel.trySendAction(
SearchAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.CopyNumberClick(number = number),
ListingItemOverflowAction.VaultAction.CopyNumberClick(
number = number,
requiresPasswordReprompt = true,
),
),
)
verify(exactly = 1) {
@ -726,7 +730,10 @@ class SearchViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
viewModel.trySendAction(
SearchAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.CopyPasswordClick(password = password),
ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = password,
requiresPasswordReprompt = true,
),
),
)
verify(exactly = 1) {
@ -744,6 +751,7 @@ class SearchViewModelTest : BaseViewModelTest() {
SearchAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.CopySecurityCodeClick(
securityCode = securityCode,
requiresPasswordReprompt = true,
),
),
)
@ -777,7 +785,10 @@ class SearchViewModelTest : BaseViewModelTest() {
viewModel.eventFlow.test {
viewModel.trySendAction(
SearchAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.EditClick(cipherId = cipherId),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = cipherId,
requiresPasswordReprompt = true,
),
),
)
assertEquals(SearchEvent.NavigateToEditCipher(cipherId), awaitItem())
@ -834,6 +845,7 @@ class SearchViewModelTest : BaseViewModelTest() {
baseIconUrl = "https://vault.bitwarden.com/icons",
isIconLoadingDisabled = false,
isAutofill = false,
hasMasterPassword = true,
)
} returns expectedViewState
val dataState = DataState.Loaded(
@ -934,6 +946,7 @@ class SearchViewModelTest : BaseViewModelTest() {
baseIconUrl = "https://vault.bitwarden.com/icons",
isIconLoadingDisabled = false,
isAutofill = false,
hasMasterPassword = true,
)
} returns expectedViewState
mutableVaultDataStateFlow.tryEmit(
@ -1044,6 +1057,7 @@ class SearchViewModelTest : BaseViewModelTest() {
baseIconUrl = "https://vault.bitwarden.com/icons",
isIconLoadingDisabled = false,
isAutofill = false,
hasMasterPassword = true,
)
} returns expectedViewState
val dataState = DataState.Error(
@ -1154,6 +1168,7 @@ class SearchViewModelTest : BaseViewModelTest() {
baseIconUrl = "https://vault.bitwarden.com/icons",
isIconLoadingDisabled = false,
isAutofill = false,
hasMasterPassword = true,
)
} returns expectedViewState
val dataState = DataState.NoNetwork(
@ -1320,6 +1335,7 @@ class SearchViewModelTest : BaseViewModelTest() {
baseIconUrl = "https://vault.bitwarden.com/icons",
isIconLoadingDisabled = false,
isAutofill = true,
hasMasterPassword = true,
)
} returns expectedViewState
val dataState = DataState.Loaded(
@ -1351,6 +1367,7 @@ private val DEFAULT_STATE: SearchState = SearchState(
baseWebSendUrl = "https://vault.bitwarden.com/#/send/",
baseIconUrl = "https://vault.bitwarden.com/icons",
isIconLoadingDisabled = false,
hasMasterPassword = true,
)
private val DEFAULT_USER_STATE = UserState(

View file

@ -271,6 +271,7 @@ class SearchTypeDataExtensionsTest {
baseIconUrl = "www.test.com",
isIconLoadingDisabled = false,
isAutofill = false,
hasMasterPassword = true,
)
assertEquals(SearchState.ViewState.Empty(message = null), result)
@ -294,6 +295,7 @@ class SearchTypeDataExtensionsTest {
baseIconUrl = "https://vault.bitwarden.com/icons",
isIconLoadingDisabled = false,
isAutofill = false,
hasMasterPassword = true,
)
assertEquals(
@ -332,6 +334,7 @@ class SearchTypeDataExtensionsTest {
baseIconUrl = "https://vault.bitwarden.com/icons",
isIconLoadingDisabled = false,
isAutofill = true,
hasMasterPassword = true,
)
assertEquals(
@ -380,6 +383,7 @@ class SearchTypeDataExtensionsTest {
baseIconUrl = "www.test.com",
isIconLoadingDisabled = false,
isAutofill = false,
hasMasterPassword = true,
)
assertEquals(

View file

@ -42,12 +42,16 @@ fun createMockDisplayItemForCipher(
),
overflowOptions = listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-$number",
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyUsernameClick(
username = "mockUsername-$number",
),
ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = "mockPassword-$number",
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyTotpClick(
totpCode = "mockTotp-$number",
@ -85,7 +89,10 @@ fun createMockDisplayItemForCipher(
),
overflowOptions = listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-$number",
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyNoteClick(
notes = "mockNotes-$number",
),
@ -119,12 +126,17 @@ fun createMockDisplayItemForCipher(
),
overflowOptions = listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-$number",
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyNumberClick(
number = "mockNumber-$number",
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopySecurityCodeClick(
securityCode = "mockCode-$number",
requiresPasswordReprompt = true,
),
),
overflowTestTag = "CipherOptionsButton",
@ -156,7 +168,10 @@ fun createMockDisplayItemForCipher(
),
overflowOptions = listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-$number",
requiresPasswordReprompt = true,
),
),
overflowTestTag = "CipherOptionsButton",
totpCode = null,

View file

@ -142,6 +142,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
@ -163,6 +164,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -180,6 +182,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginState
@ -221,6 +224,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginState
@ -251,6 +255,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns loginViewState
@ -292,6 +297,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -336,6 +342,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns loginViewState
@ -374,6 +381,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns DEFAULT_VIEW_STATE
@ -412,6 +420,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns DEFAULT_VIEW_STATE
@ -459,6 +468,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns DEFAULT_VIEW_STATE
@ -489,6 +499,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -521,6 +532,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -582,6 +594,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -633,6 +646,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -695,6 +709,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
@ -716,6 +731,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -729,6 +745,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false))
@ -744,6 +761,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
clipboardManager.setText(text = field)
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -776,6 +794,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
@ -805,6 +824,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -835,6 +855,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -864,6 +885,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -877,6 +899,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
@ -898,6 +921,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -915,6 +939,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -947,6 +972,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -970,6 +996,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -991,6 +1018,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -1037,6 +1065,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
@ -1058,6 +1087,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -1075,6 +1105,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -1104,6 +1135,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
@ -1125,6 +1157,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -1142,6 +1175,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -1182,6 +1216,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -1236,6 +1271,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -1299,6 +1335,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns loginViewState
@ -1475,6 +1512,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns DEFAULT_VIEW_STATE
@ -1513,6 +1551,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
}
@ -1529,6 +1568,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns DEFAULT_VIEW_STATE
@ -1553,6 +1593,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
}
@ -1565,6 +1606,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false))
@ -1581,6 +1623,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
clipboardManager.setText(text = DEFAULT_LOGIN_PASSWORD)
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
}
@ -1619,6 +1662,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false))
@ -1633,6 +1677,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
clipboardManager.setText(text = DEFAULT_LOGIN_USERNAME)
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
}
@ -1655,6 +1700,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns DEFAULT_VIEW_STATE
@ -1677,6 +1723,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
}
@ -1690,6 +1737,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
}
@ -1714,6 +1762,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
}
@ -1728,6 +1777,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns DEFAULT_VIEW_STATE
@ -1754,6 +1804,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
}
@ -1771,6 +1822,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
} returns loginViewState
@ -1800,6 +1852,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
)
}
@ -1827,6 +1880,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns CARD_VIEW_STATE
@ -1850,6 +1904,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -1862,6 +1917,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns createViewState(
@ -1879,6 +1935,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
clipboardManager.setText(text = "12345436")
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -1892,6 +1949,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns CARD_VIEW_STATE
@ -1915,6 +1973,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
verify(exactly = 1) {
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -1927,6 +1986,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
every {
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns createViewState(
@ -1944,6 +2004,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
clipboardManager.setText(text = "987")
mockCipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
}
@ -1975,7 +2036,11 @@ class VaultItemViewModelTest : BaseViewModelTest() {
val viewState = mockk<VaultItemState.ViewState>()
val cipherView = mockk<CipherView> {
every {
toViewState(isPremiumUser = true, totpCodeItemData = null)
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns viewState
}
val viewModel = createViewModel(state = null)
@ -2006,7 +2071,11 @@ class VaultItemViewModelTest : BaseViewModelTest() {
val viewState = mockk<VaultItemState.ViewState>()
val cipherView = mockk<CipherView> {
every {
toViewState(isPremiumUser = true, totpCodeItemData = null)
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns viewState
}
val viewModel = createViewModel(state = null)
@ -2038,7 +2107,11 @@ class VaultItemViewModelTest : BaseViewModelTest() {
val viewState = mockk<VaultItemState.ViewState>()
val cipherView = mockk<CipherView> {
every {
toViewState(isPremiumUser = true, totpCodeItemData = null)
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns viewState
}
val viewModel = createViewModel(state = null)
@ -2069,7 +2142,11 @@ class VaultItemViewModelTest : BaseViewModelTest() {
val viewState = mockk<VaultItemState.ViewState>()
val cipherView = mockk<CipherView> {
every {
toViewState(isPremiumUser = true, totpCodeItemData = null)
toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
)
} returns viewState
}
val viewModel = createViewModel(state = null)

View file

@ -16,12 +16,41 @@ class CipherViewExtensionsTest {
ZoneOffset.UTC,
)
@Suppress("MaxLineLength")
@Test
fun `toViewState should transform full CipherView into ViewState Login Content without master password reprompt`() {
val cipherView = createCipherView(type = CipherType.LOGIN, isEmpty = false)
val viewState = cipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = false,
totpCodeItemData = TotpCodeItemData(
periodSeconds = 30,
timeLeftSeconds = 15,
verificationCode = "123456",
totpCode = "testCode",
),
clock = fixedClock,
)
assertEquals(
VaultItemState.ViewState.Content(
common = createCommonContent(isEmpty = false, isPremiumUser = true).copy(
currentCipher = cipherView,
requiresReprompt = false,
),
type = createLoginContent(isEmpty = false),
),
viewState,
)
}
@Test
fun `toViewState should transform full CipherView into ViewState Login Content with premium`() {
val cipherView = createCipherView(type = CipherType.LOGIN, isEmpty = false)
val viewState = cipherView.toViewState(
isPremiumUser = true,
TotpCodeItemData(
hasMasterPassword = true,
totpCodeItemData = TotpCodeItemData(
periodSeconds = 30,
timeLeftSeconds = 15,
verificationCode = "123456",
@ -47,6 +76,7 @@ class CipherViewExtensionsTest {
val cipherView = createCipherView(type = CipherType.LOGIN, isEmpty = false)
val viewState = cipherView.toViewState(
isPremiumUser = isPremiumUser,
hasMasterPassword = true,
totpCodeItemData = TotpCodeItemData(
periodSeconds = 30,
timeLeftSeconds = 15,
@ -71,6 +101,7 @@ class CipherViewExtensionsTest {
val cipherView = createCipherView(type = CipherType.LOGIN, isEmpty = true)
val viewState = cipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
clock = fixedClock,
)
@ -91,6 +122,7 @@ class CipherViewExtensionsTest {
val cipherView = createCipherView(type = CipherType.IDENTITY, isEmpty = false)
val viewState = cipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
clock = fixedClock,
)
@ -110,6 +142,7 @@ class CipherViewExtensionsTest {
val cipherView = createCipherView(type = CipherType.IDENTITY, isEmpty = true)
val viewState = cipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
clock = fixedClock,
)
@ -139,6 +172,7 @@ class CipherViewExtensionsTest {
)
val viewState = cipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
clock = fixedClock,
)
@ -173,6 +207,7 @@ class CipherViewExtensionsTest {
)
val result = cipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
clock = fixedClock,
)
@ -209,6 +244,7 @@ class CipherViewExtensionsTest {
val cipherView = createCipherView(type = CipherType.SECURE_NOTE, isEmpty = false)
val viewState = cipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
clock = fixedClock,
)
@ -229,6 +265,7 @@ class CipherViewExtensionsTest {
val cipherView = createCipherView(type = CipherType.SECURE_NOTE, isEmpty = true)
val viewState = cipherView.toViewState(
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
clock = fixedClock,
)

View file

@ -1196,6 +1196,7 @@ class VaultItemListingScreenTest : BaseComposeTest() {
VaultItemListingsAction.OverflowOptionClick(
action = ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-1",
requiresPasswordReprompt = true,
),
),
)
@ -1508,6 +1509,7 @@ private val DEFAULT_STATE = VaultItemListingState(
isPullToRefreshSettingEnabled = false,
dialogState = null,
policyDisablesSend = false,
hasMasterPassword = true,
)
private val STATE_FOR_AUTOFILL = DEFAULT_STATE.copy(
@ -1567,7 +1569,10 @@ private fun createCipherDisplayItem(number: Int): VaultItemListingState.DisplayI
iconData = IconData.Local(R.drawable.ic_vault),
extraIconList = emptyList(),
overflowOptions = listOf(
ListingItemOverflowAction.VaultAction.EditClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-$number",
requiresPasswordReprompt = true,
),
),
optionsTestTag = "CipherOptionsButton",
isAutofill = false,

View file

@ -448,6 +448,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
masterPasswordRepromptData = MasterPasswordRepromptData.OverflowItem(
action = ListingItemOverflowAction.VaultAction.EditClick(
cipherId = cipherId,
requiresPasswordReprompt = true,
),
),
),
@ -688,7 +689,10 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
val viewModel = createVaultItemListingViewModel()
viewModel.trySendAction(
VaultItemListingsAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.CopyNumberClick(number = number),
ListingItemOverflowAction.VaultAction.CopyNumberClick(
number = number,
requiresPasswordReprompt = true,
),
),
)
verify(exactly = 1) {
@ -704,7 +708,10 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
val viewModel = createVaultItemListingViewModel()
viewModel.trySendAction(
VaultItemListingsAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.CopyPasswordClick(password = password),
ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = password,
requiresPasswordReprompt = true,
),
),
)
verify(exactly = 1) {
@ -722,6 +729,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
VaultItemListingsAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.CopySecurityCodeClick(
securityCode = securityCode,
requiresPasswordReprompt = true,
),
),
)
@ -800,7 +808,10 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
viewModel.eventFlow.test {
viewModel.trySendAction(
VaultItemListingsAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.EditClick(cipherId = cipherId),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = cipherId,
requiresPasswordReprompt = true,
),
),
)
assertEquals(VaultItemListingEvent.NavigateToEditCipher(cipherId), awaitItem())
@ -1443,6 +1454,7 @@ class VaultItemListingViewModelTest : BaseViewModelTest() {
autofillSelectionData = null,
shouldFinishOnComplete = false,
policyDisablesSend = false,
hasMasterPassword = true,
)
}

View file

@ -393,6 +393,7 @@ class VaultItemListingDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
autofillSelectionData = null,
hasMasterPassword = true,
)
assertEquals(
@ -466,6 +467,7 @@ class VaultItemListingDataExtensionsTest {
type = AutofillSelectionData.Type.LOGIN,
uri = null,
),
hasMasterPassword = true,
)
assertEquals(
@ -516,6 +518,7 @@ class VaultItemListingDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
autofillSelectionData = null,
hasMasterPassword = true,
),
)
@ -533,6 +536,7 @@ class VaultItemListingDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
autofillSelectionData = null,
hasMasterPassword = true,
),
)
@ -548,6 +552,7 @@ class VaultItemListingDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
autofillSelectionData = null,
hasMasterPassword = true,
),
)
@ -566,6 +571,7 @@ class VaultItemListingDataExtensionsTest {
type = AutofillSelectionData.Type.LOGIN,
uri = "https://www.test.com",
),
hasMasterPassword = true,
),
)
}
@ -704,6 +710,7 @@ class VaultItemListingDataExtensionsTest {
autofillSelectionData = null,
itemListingType = VaultItemListingState.ItemListingType.Vault.Folder("1"),
vaultFilterType = VaultFilterType.AllVaults,
hasMasterPassword = true,
)
assertEquals(
@ -743,6 +750,7 @@ class VaultItemListingDataExtensionsTest {
autofillSelectionData = null,
itemListingType = VaultItemListingState.ItemListingType.Vault.Collection("mockId-1"),
vaultFilterType = VaultFilterType.AllVaults,
hasMasterPassword = true,
)
assertEquals(

View file

@ -43,12 +43,16 @@ fun createMockDisplayItemForCipher(
),
overflowOptions = listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-$number",
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyUsernameClick(
username = "mockUsername-$number",
),
ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = "mockPassword-$number",
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyTotpClick(
totpCode = "mockTotp-$number",
@ -86,7 +90,10 @@ fun createMockDisplayItemForCipher(
),
overflowOptions = listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-$number",
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyNoteClick(
notes = "mockNotes-$number",
),
@ -120,12 +127,17 @@ fun createMockDisplayItemForCipher(
),
overflowOptions = listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-$number",
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyNumberClick(
number = "mockNumber-$number",
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopySecurityCodeClick(
securityCode = "mockCode-$number",
requiresPasswordReprompt = true,
),
),
optionsTestTag = "CipherOptionsButton",
@ -157,7 +169,10 @@ fun createMockDisplayItemForCipher(
),
overflowOptions = listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = "mockId-$number"),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = "mockId-$number",
requiresPasswordReprompt = true,
),
),
optionsTestTag = "CipherOptionsButton",
isAutofill = false,

View file

@ -31,14 +31,20 @@ class CipherViewExtensionsTest {
),
)
val result = cipher.toOverflowActions()
val result = cipher.toOverflowActions(hasMasterPassword = false)
assertEquals(
listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = id),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = id),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = id,
requiresPasswordReprompt = false,
),
ListingItemOverflowAction.VaultAction.CopyUsernameClick(username = username),
ListingItemOverflowAction.VaultAction.CopyPasswordClick(password = password),
ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = password,
requiresPasswordReprompt = false,
),
ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode = totpCode),
ListingItemOverflowAction.VaultAction.LaunchClick(url = uri),
),
@ -63,12 +69,15 @@ class CipherViewExtensionsTest {
),
viewPassword = false,
)
val result = cipher.toOverflowActions()
val result = cipher.toOverflowActions(hasMasterPassword = true)
assertEquals(
listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = id),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = id),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = id,
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyUsernameClick(username = username),
ListingItemOverflowAction.VaultAction.CopyTotpClick(totpCode = totpCode),
ListingItemOverflowAction.VaultAction.LaunchClick(url = uri),
@ -95,7 +104,7 @@ class CipherViewExtensionsTest {
),
)
val result = cipher.toOverflowActions()
val result = cipher.toOverflowActions(hasMasterPassword = true)
assertEquals(
listOf(ListingItemOverflowAction.VaultAction.ViewClick(cipherId = id)),
@ -116,15 +125,22 @@ class CipherViewExtensionsTest {
),
)
val result = cipher.toOverflowActions()
val result = cipher.toOverflowActions(hasMasterPassword = true)
assertEquals(
listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = id),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = id),
ListingItemOverflowAction.VaultAction.CopyNumberClick(number = number),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = id,
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyNumberClick(
number = number,
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopySecurityCodeClick(
securityCode = securityCode,
requiresPasswordReprompt = true,
),
),
result,
@ -147,7 +163,7 @@ class CipherViewExtensionsTest {
),
)
val result = cipher.toOverflowActions()
val result = cipher.toOverflowActions(hasMasterPassword = false)
assertEquals(
listOf(ListingItemOverflowAction.VaultAction.ViewClick(cipherId = id)),
@ -163,12 +179,15 @@ class CipherViewExtensionsTest {
identity = createMockIdentityView(number = 1),
)
val result = cipher.toOverflowActions()
val result = cipher.toOverflowActions(hasMasterPassword = false)
assertEquals(
listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = id),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = id),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = id,
requiresPasswordReprompt = false,
),
),
result,
)
@ -187,7 +206,7 @@ class CipherViewExtensionsTest {
identity = createMockIdentityView(number = 1),
)
val result = cipher.toOverflowActions()
val result = cipher.toOverflowActions(hasMasterPassword = true)
assertEquals(
listOf(ListingItemOverflowAction.VaultAction.ViewClick(cipherId = id)),
@ -205,12 +224,15 @@ class CipherViewExtensionsTest {
notes = notes,
)
val result = cipher.toOverflowActions()
val result = cipher.toOverflowActions(hasMasterPassword = true)
assertEquals(
listOf(
ListingItemOverflowAction.VaultAction.ViewClick(cipherId = id),
ListingItemOverflowAction.VaultAction.EditClick(cipherId = id),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = id,
requiresPasswordReprompt = true,
),
ListingItemOverflowAction.VaultAction.CopyNoteClick(notes = notes),
),
result,
@ -231,7 +253,7 @@ class CipherViewExtensionsTest {
notes = null,
)
val result = cipher.toOverflowActions()
val result = cipher.toOverflowActions(hasMasterPassword = false)
assertEquals(
listOf(ListingItemOverflowAction.VaultAction.ViewClick(cipherId = id)),

View file

@ -1177,6 +1177,7 @@ private val DEFAULT_STATE: VaultState = VaultState(
isPullToRefreshSettingEnabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
isIconLoadingDisabled = false,
hasMasterPassword = true,
)
private val DEFAULT_CONTENT_VIEW_STATE: VaultState.ViewState.Content = VaultState.ViewState.Content(

View file

@ -476,6 +476,7 @@ class VaultViewModelTest : BaseViewModelTest() {
vaultFilterType = VaultFilterType.AllVaults,
isIconLoadingDisabled = viewModel.stateFlow.value.isIconLoadingDisabled,
baseIconUrl = viewModel.stateFlow.value.baseIconUrl,
hasMasterPassword = true,
),
)
.copy(
@ -499,6 +500,7 @@ class VaultViewModelTest : BaseViewModelTest() {
vaultFilterType = VaultFilterType.MyVault,
isIconLoadingDisabled = viewModel.stateFlow.value.isIconLoadingDisabled,
baseIconUrl = viewModel.stateFlow.value.baseIconUrl,
hasMasterPassword = true,
),
),
viewModel.stateFlow.value,
@ -1186,7 +1188,10 @@ class VaultViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
viewModel.trySendAction(
VaultAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.CopyNumberClick(number = number),
ListingItemOverflowAction.VaultAction.CopyNumberClick(
number = number,
requiresPasswordReprompt = true,
),
),
)
verify(exactly = 1) {
@ -1202,7 +1207,10 @@ class VaultViewModelTest : BaseViewModelTest() {
val viewModel = createViewModel()
viewModel.trySendAction(
VaultAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.CopyPasswordClick(password = password),
ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = password,
requiresPasswordReprompt = true,
),
),
)
verify(exactly = 1) {
@ -1265,6 +1273,7 @@ class VaultViewModelTest : BaseViewModelTest() {
VaultAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.CopySecurityCodeClick(
securityCode = securityCode,
requiresPasswordReprompt = true,
),
),
)
@ -1298,7 +1307,10 @@ class VaultViewModelTest : BaseViewModelTest() {
viewModel.eventFlow.test {
viewModel.trySendAction(
VaultAction.OverflowOptionClick(
ListingItemOverflowAction.VaultAction.EditClick(cipherId = cipherId),
ListingItemOverflowAction.VaultAction.EditClick(
cipherId = cipherId,
requiresPasswordReprompt = true,
),
),
)
assertEquals(VaultEvent.NavigateToEditVaultItem(cipherId), awaitItem())
@ -1352,6 +1364,7 @@ class VaultViewModelTest : BaseViewModelTest() {
VaultAction.MasterPasswordRepromptSubmit(
overflowAction = ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = password,
requiresPasswordReprompt = true,
),
password = password,
),
@ -1389,6 +1402,7 @@ class VaultViewModelTest : BaseViewModelTest() {
VaultAction.MasterPasswordRepromptSubmit(
overflowAction = ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = password,
requiresPasswordReprompt = true,
),
password = password,
),
@ -1421,6 +1435,7 @@ class VaultViewModelTest : BaseViewModelTest() {
VaultAction.MasterPasswordRepromptSubmit(
overflowAction = ListingItemOverflowAction.VaultAction.CopyPasswordClick(
password = password,
requiresPasswordReprompt = true,
),
password = password,
),
@ -1532,4 +1547,5 @@ private fun createMockVaultState(
isPullToRefreshSettingEnabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
isIconLoadingDisabled = false,
hasMasterPassword = true,
)

View file

@ -55,6 +55,7 @@ class VaultDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
vaultFilterType = VaultFilterType.AllVaults,
hasMasterPassword = true,
)
assertEquals(
@ -115,6 +116,7 @@ class VaultDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
vaultFilterType = VaultFilterType.MyVault,
hasMasterPassword = true,
)
assertEquals(
@ -167,6 +169,7 @@ class VaultDataExtensionsTest {
organizationId = "mockOrganizationId-1",
organizationName = "Mock Organization 1",
),
hasMasterPassword = true,
)
assertEquals(
@ -212,6 +215,7 @@ class VaultDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
vaultFilterType = VaultFilterType.AllVaults,
hasMasterPassword = true,
)
assertEquals(
@ -234,6 +238,7 @@ class VaultDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
vaultFilterType = VaultFilterType.AllVaults,
hasMasterPassword = true,
)
assertEquals(
@ -257,6 +262,7 @@ class VaultDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
vaultFilterType = VaultFilterType.AllVaults,
hasMasterPassword = true,
)
assertEquals(
@ -291,6 +297,7 @@ class VaultDataExtensionsTest {
vaultFilterType = VaultFilterType.AllVaults,
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
hasMasterPassword = true,
)
assertEquals(
@ -439,6 +446,7 @@ class VaultDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
vaultFilterType = VaultFilterType.AllVaults,
hasMasterPassword = true,
)
assertEquals(
@ -479,6 +487,7 @@ class VaultDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
vaultFilterType = VaultFilterType.AllVaults,
hasMasterPassword = true,
)
assertEquals(
@ -531,6 +540,7 @@ class VaultDataExtensionsTest {
isIconLoadingDisabled = false,
baseIconUrl = Environment.Us.environmentUrlData.baseIconUrl,
vaultFilterType = VaultFilterType.AllVaults,
hasMasterPassword = true,
)
assertEquals(