[PM-13360] Respect manage permission to assign collections

This commit prevents users from assigning items to collections if the item is already in a read-only collection where the user does not have "manage" permission.

This change ensures that users with limited permissions cannot modify items in a way that violates the collection's access controls.
This commit is contained in:
Patrick Honkonen 2024-10-23 13:47:55 -04:00
parent 98506076d3
commit 76954d456e
No known key found for this signature in database
GPG key ID: B63AF42A5531C877
15 changed files with 564 additions and 44 deletions

View file

@ -235,7 +235,7 @@ fun VaultAddEditScreen(
},
)
if (pendingDeleteCipher) {
if (pendingDeleteCipher && state.canDelete) {
BitwardenTwoButtonDialog(
title = stringResource(id = R.string.delete),
message = stringResource(id = R.string.do_you_really_want_to_soft_delete_cipher),
@ -309,7 +309,11 @@ fun VaultAddEditScreen(
}
},
)
.takeUnless { state.isAddItemMode || !state.isCipherInCollection },
.takeUnless {
state.isAddItemMode ||
(!state.isCipherInCollection ||
!state.canAssociateToCollections)
},
OverflowMenuItemData(
text = stringResource(id = R.string.delete),
onClick = { pendingDeleteCipher = true },

View file

@ -1621,16 +1621,29 @@ class VaultAddEditViewModel @Inject constructor(
currentAccount = userData?.activeAccount,
vaultAddEditType = vaultAddEditType,
) { currentAccount, cipherView ->
// Deletion is not allowed when the item is in a collection that the user
// does not have "manage" permission for.
val canDelete = vaultData.collectionViewList
.none {
val itemIsInCollection = cipherView
val isItemInCollection = cipherView
?.collectionIds
?.contains(it.id) == true
val canManageCollection = it.manage
itemIsInCollection && !canManageCollection
isItemInCollection && !it.manage
}
// Assigning to a collection is not allowed when the item is in a collection
// that the user does not have "manage" and "edit" permission for.
val canAssignToCollections = vaultData.collectionViewList
.none {
val isItemInCollection = cipherView
?.collectionIds
?.contains(it.id) == true
isItemInCollection && (!it.manage || it.readOnly)
}
// Derive the view state from the current Cipher for Edit mode
// or use the current state for Add
(cipherView
@ -1641,6 +1654,7 @@ class VaultAddEditViewModel @Inject constructor(
resourceManager = resourceManager,
clock = clock,
canDelete = canDelete,
canAssignToCollections = canAssignToCollections,
)
?: viewState)
.appendFolderAndOwnerData(
@ -2078,6 +2092,12 @@ data class VaultAddEditState(
?.canDelete
?: false
val canAssociateToCollections: Boolean
get() = (viewState as? ViewState.Content)
?.common
?.canAssignToCollections
?: false
/**
* Enum representing the main type options for the vault, such as LOGIN, CARD, etc.
*
@ -2153,6 +2173,7 @@ data class VaultAddEditState(
val selectedOwnerId: String? = null,
val availableOwners: List<Owner> = emptyList(),
val canDelete: Boolean = true,
val canAssignToCollections: Boolean = true,
) : Parcelable {
/**

View file

@ -43,6 +43,7 @@ fun CipherView.toViewState(
resourceManager: ResourceManager,
clock: Clock,
canDelete: Boolean,
canAssignToCollections: Boolean,
): VaultAddEditState.ViewState =
VaultAddEditState.ViewState.Content(
type = when (type) {
@ -109,6 +110,7 @@ fun CipherView.toViewState(
availableOwners = emptyList(),
customFieldData = this.fields.orEmpty().map { it.toCustomField() },
canDelete = canDelete,
canAssignToCollections = canAssignToCollections,
),
isIndividualVaultDisabled = isIndividualVaultDisabled,
)

View file

@ -163,7 +163,7 @@ fun VaultItemScreen(
{ viewModel.trySendAction(VaultItemAction.Common.CloseClick) }
},
actions = {
if (state.isCipherDeleted) {
if (state.isCipherDeleted && state.canDelete) {
BitwardenTextButton(
label = stringResource(id = R.string.restore),
onClick = remember(viewModel) {
@ -216,7 +216,10 @@ fun VaultItemScreen(
}
},
)
.takeIf { state.isCipherInCollection },
.takeIf {
state.isCipherInCollection &&
state.canAssignToCollections
},
OverflowMenuItemData(
text = stringResource(id = R.string.delete),
onClick = remember(viewModel) {

View file

@ -111,9 +111,19 @@ class VaultItemViewModel @Inject constructor(
?.collectionIds
?.contains(it.id) == true
val canManageCollection = it.manage
itemIsInCollection && !it.manage
}
?: true
itemIsInCollection && !canManageCollection
// Assigning to a collection is not allowed when the item is in a collection
// that the user does not have "manage" and "edit" permission for.
val canAssignToCollections = collectionsState.data
?.none {
val itemIsInCollection = cipherViewState.data
?.collectionIds
?.contains(it.id) == true
itemIsInCollection && !it.manage && it.readOnly
}
?: true
@ -121,6 +131,7 @@ class VaultItemViewModel @Inject constructor(
cipher = cipherViewState.data,
totpCodeItemData = totpCodeData,
canDelete = canDelete,
canAssociateToCollections = canAssignToCollections,
)
},
)
@ -917,6 +928,7 @@ class VaultItemViewModel @Inject constructor(
hasMasterPassword = account.hasMasterPassword,
totpCodeItemData = this.data?.totpCodeItemData,
canDelete = this.data?.canDelete == true,
canAssignToCollections = this.data?.canAssociateToCollections == true,
)
?: VaultItemState.ViewState.Error(message = errorText)
@ -1163,6 +1175,12 @@ data class VaultItemState(
?.common
?.canDelete == true
val canAssignToCollections: Boolean
get() = viewState.asContentOrNull()
?.common
?.canAssignToCollections
?: false
/**
* The text to display on the deletion confirmation dialog.
*/
@ -1214,6 +1232,10 @@ data class VaultItemState(
* @property requiresCloneConfirmation Indicates user confirmation is required when
* cloning a cipher.
* @property currentCipher The cipher that is currently being viewed (nullable).
* @property attachments A list of attachments associated with the cipher.
* @property canDelete Indicates if the cipher can be deleted.
* @property canAssignToCollections Indicates if the cipher can be assigned to
* collections.
*/
@Parcelize
data class Common(
@ -1227,6 +1249,7 @@ data class VaultItemState(
val currentCipher: CipherView? = null,
val attachments: List<AttachmentItem>?,
val canDelete: Boolean,
val canAssignToCollections: Boolean,
) : Parcelable {
/**

View file

@ -7,9 +7,12 @@ import com.bitwarden.vault.CipherView
*
* @property cipher The cipher view for the item.
* @property totpCodeItemData The data for the totp code.
* @property canDelete Whether the item can be deleted.
* @property canAssociateToCollections Whether the item can be associated to a collection.
*/
data class VaultItemStateData(
val cipher: CipherView?,
val totpCodeItemData: TotpCodeItemData?,
val canDelete: Boolean,
val canAssociateToCollections: Boolean,
)

View file

@ -40,6 +40,7 @@ fun CipherView.toViewState(
totpCodeItemData: TotpCodeItemData?,
clock: Clock = Clock.systemDefaultZone(),
canDelete: Boolean,
canAssignToCollections: Boolean,
): VaultItemState.ViewState =
VaultItemState.ViewState.Content(
common = VaultItemState.ViewState.Content.Common(
@ -81,6 +82,7 @@ fun CipherView.toViewState(
}
.orEmpty(),
canDelete = canDelete,
canAssignToCollections = canAssignToCollections,
),
type = when (type) {
CipherType.LOGIN -> {

View file

@ -8,6 +8,7 @@ import com.bitwarden.vault.CollectionView
fun createMockCollectionView(
number: Int,
name: String? = null,
readOnly: Boolean = false,
manage: Boolean = true,
): CollectionView =
CollectionView(
@ -16,6 +17,6 @@ fun createMockCollectionView(
hidePasswords = false,
name = name ?: "mockName-$number",
externalId = "mockExternalId-$number",
readOnly = false,
readOnly = readOnly,
manage = manage,
)

View file

@ -12,6 +12,7 @@ import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.click
import androidx.compose.ui.test.filter
import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.hasContentDescription
@ -3023,6 +3024,57 @@ class VaultAddEditScreenTest : BaseComposeTest() {
.assertIsDisplayed()
}
@Test
fun `Menu Collections should display correctly according to state`() {
mutableStateFlow.update {
it.copy(
vaultAddEditType = VaultAddEditType.EditItem(vaultItemId = "mockId-1"),
viewState = VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(
originalCipher = createMockCipherView(1),
),
type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
isIndividualVaultDisabled = false,
),
)
}
// Confirm overflow is closed on initial load
composeTestRule
.onAllNodesWithText("Collections")
.filter(hasAnyAncestor(isPopup()))
.assertCountEquals(0)
// Open the overflow menu
composeTestRule
.onNodeWithContentDescription("More")
.performClick()
// Confirm Collections option is present
composeTestRule
.onAllNodesWithText("Collections")
.filterToOne(hasAnyAncestor(isPopup()))
.assertIsDisplayed()
// Confirm Collections option is not present when canAssignToCollections is false
mutableStateFlow.update {
it.copy(
vaultAddEditType = VaultAddEditType.EditItem(vaultItemId = "mockId-1"),
viewState = VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(
originalCipher = createMockCipherView(1),
canAssignToCollections = false,
),
type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
isIndividualVaultDisabled = false,
),
)
}
composeTestRule
.onAllNodesWithText("Collections")
.filter(hasAnyAncestor(isPopup()))
.assertCountEquals(0)
}
@Test
fun `Menu should display correct items when cipher is not in a collection`() {
mutableStateFlow.update {

View file

@ -1227,6 +1227,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = false,
canAssignToCollections = true,
)
} returns stateWithName.viewState
@ -1257,6 +1258,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = false,
canAssignToCollections = false,
)
}
}
@ -1281,6 +1283,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
),
notes = "mockNotes-1",
canDelete = true,
canAssociateToCollections = true,
),
)
@ -1292,6 +1295,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
} returns stateWithName.viewState
@ -1302,6 +1306,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
createMockCollectionView(
number = 1,
manage = true,
readOnly = false,
),
),
),
@ -1322,6 +1327,214 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
}
}
@Suppress("MaxLineLength")
@Test
fun `in edit mode, canAssociateToCollections should be false when cipher is in a collection the user cannot manage or edit`() =
runTest {
val cipherView = createMockCipherView(1)
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
val stateWithName = createVaultAddItemState(
vaultAddEditType = vaultAddEditType,
commonContentViewState = createCommonContentViewState(
name = "mockName-1",
originalCipher = cipherView,
customFieldData = listOf(
VaultAddEditState.Custom.HiddenField(
itemId = "testId",
name = "mockName-1",
value = "mockValue-1",
),
),
notes = "mockNotes-1",
canDelete = false,
canAssociateToCollections = false,
),
)
every {
cipherView.toViewState(
isClone = false,
isIndividualVaultDisabled = false,
totpData = null,
resourceManager = resourceManager,
clock = fixedClock,
canDelete = false,
canAssignToCollections = false,
)
} returns stateWithName.viewState
mutableVaultDataFlow.value = DataState.Loaded(
data = createVaultData(
cipherView = cipherView,
collectionViewList = listOf(
createMockCollectionView(
number = 1,
readOnly = true,
manage = false,
),
),
),
)
createAddVaultItemViewModel(
createSavedStateHandleWithState(
state = stateWithName,
vaultAddEditType = vaultAddEditType,
),
)
verify {
cipherView.toViewState(
isClone = false,
isIndividualVaultDisabled = false,
totpData = null,
resourceManager = resourceManager,
clock = fixedClock,
canDelete = false,
canAssignToCollections = false,
)
}
}
@Suppress("MaxLineLength")
@Test
fun `in edit mode, canAssociateToCollections should be false when cipher is in a collection the user cannot manage but can edit`() =
runTest {
val cipherView = createMockCipherView(1)
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
val stateWithName = createVaultAddItemState(
vaultAddEditType = vaultAddEditType,
commonContentViewState = createCommonContentViewState(
name = "mockName-1",
originalCipher = cipherView,
customFieldData = listOf(
VaultAddEditState.Custom.HiddenField(
itemId = "testId",
name = "mockName-1",
value = "mockValue-1",
),
),
notes = "mockNotes-1",
canDelete = false,
canAssociateToCollections = false,
),
)
every {
cipherView.toViewState(
isClone = false,
isIndividualVaultDisabled = false,
totpData = null,
resourceManager = resourceManager,
clock = fixedClock,
canDelete = false,
canAssignToCollections = false,
)
} returns stateWithName.viewState
mutableVaultDataFlow.value = DataState.Loaded(
data = createVaultData(
cipherView = cipherView,
collectionViewList = listOf(
createMockCollectionView(
number = 1,
manage = false,
readOnly = false,
),
),
),
)
createAddVaultItemViewModel(
createSavedStateHandleWithState(
state = stateWithName,
vaultAddEditType = vaultAddEditType,
),
)
verify {
cipherView.toViewState(
isClone = false,
isIndividualVaultDisabled = false,
totpData = null,
resourceManager = resourceManager,
clock = fixedClock,
canDelete = false,
canAssignToCollections = false,
)
}
}
@Suppress("MaxLineLength")
@Test
fun `in edit mode, canAssociateToCollections should be false when cipher is in a collection the user can manage but cannot edit`() =
runTest {
val cipherView = createMockCipherView(1)
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
val stateWithName = createVaultAddItemState(
vaultAddEditType = vaultAddEditType,
commonContentViewState = createCommonContentViewState(
name = "mockName-1",
originalCipher = cipherView,
customFieldData = listOf(
VaultAddEditState.Custom.HiddenField(
itemId = "testId",
name = "mockName-1",
value = "mockValue-1",
),
),
notes = "mockNotes-1",
canDelete = true,
canAssociateToCollections = false,
),
)
every {
cipherView.toViewState(
isClone = false,
isIndividualVaultDisabled = false,
totpData = null,
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = false,
)
} returns stateWithName.viewState
mutableVaultDataFlow.value = DataState.Loaded(
data = createVaultData(
cipherView = cipherView,
collectionViewList = listOf(
createMockCollectionView(
number = 1,
manage = true,
readOnly = true,
),
),
),
)
createAddVaultItemViewModel(
createSavedStateHandleWithState(
state = stateWithName,
vaultAddEditType = vaultAddEditType,
),
)
verify {
cipherView.toViewState(
isClone = false,
isIndividualVaultDisabled = false,
totpData = null,
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = false,
)
}
}
@ -1442,6 +1655,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
} returns stateWithName.viewState
mutableVaultDataFlow.value = DataState.Loaded(
@ -1474,6 +1688,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
vaultRepository.updateCipher(DEFAULT_EDIT_ITEM_ID, any())
}
@ -1509,6 +1724,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
} returns stateWithName.viewState
coEvery {
@ -1572,6 +1788,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
} returns stateWithName.viewState
coEvery {
@ -1638,6 +1855,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
} returns stateWithName.viewState
mutableVaultDataFlow.value = DataState.Loaded(
@ -1695,6 +1913,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
} returns stateWithName.viewState
every { fido2CredentialManager.isUserVerified } returns true
@ -1757,6 +1976,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
} returns stateWithName.viewState
every { fido2CredentialManager.isUserVerified } returns false
@ -4165,6 +4385,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
availableOwners: List<VaultAddEditState.Owner> = createOwnerList(),
selectedOwnerId: String? = null,
canDelete: Boolean = true,
canAssociateToCollections: Boolean = true,
): VaultAddEditState.ViewState.Content.Common =
VaultAddEditState.ViewState.Content.Common(
name = name,
@ -4178,6 +4399,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
availableFolders = availableFolders,
availableOwners = availableOwners,
canDelete = canDelete,
canAssignToCollections = canAssociateToCollections,
)
@Suppress("LongParameterList")

View file

@ -76,6 +76,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager,
clock = FIXED_CLOCK,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -123,6 +124,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager,
clock = FIXED_CLOCK,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -175,6 +177,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager,
clock = FIXED_CLOCK,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -236,6 +239,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager,
clock = FIXED_CLOCK,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -294,6 +298,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager,
clock = FIXED_CLOCK,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -330,6 +335,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager,
clock = FIXED_CLOCK,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -375,6 +381,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager,
clock = FIXED_CLOCK,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(

View file

@ -1105,6 +1105,50 @@ class VaultItemScreenTest : BaseComposeTest() {
}
}
@Test
fun `menu Collection option should be displayed based on state`() {
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_IDENTITY_VIEW_STATE
.copy(
common = DEFAULT_COMMON
.copy(currentCipher = createMockCipherView(1)),
),
)
}
// Confirm overflow is closed on initial load
composeTestRule
.onAllNodesWithText("Collections")
.filter(hasAnyAncestor(isPopup()))
.assertCountEquals(0)
// Open the overflow menu
composeTestRule
.onNodeWithContentDescription("More")
.performClick()
// Confirm Collections option is present
composeTestRule
.onAllNodesWithText("Collections")
.filterToOne(hasAnyAncestor(isPopup()))
.assertIsDisplayed()
// Confirm Collections option is not present when canDelete is false
mutableStateFlow.update {
it.copy(
viewState = DEFAULT_IDENTITY_VIEW_STATE
.copy(
common = DEFAULT_COMMON
.copy(canAssignToCollections = false),
),
)
}
composeTestRule
.onAllNodesWithText("Collections")
.filter(hasAnyAncestor(isPopup()))
.assertCountEquals(0)
}
@Test
fun `Collections menu click should send CollectionsClick action`() {
mutableStateFlow.update {
@ -2387,6 +2431,7 @@ private val DEFAULT_COMMON: VaultItemState.ViewState.Content.Common =
),
),
canDelete = true,
canAssignToCollections = true,
)
private val DEFAULT_PASSKEY = R.string.created_xy.asText(
@ -2469,6 +2514,7 @@ private val EMPTY_COMMON: VaultItemState.ViewState.Content.Common =
requiresCloneConfirmation = false,
attachments = emptyList(),
canDelete = true,
canAssignToCollections = true,
)
private val EMPTY_LOGIN_TYPE: VaultItemState.ViewState.Content.ItemType.Login =

View file

@ -166,6 +166,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
@ -179,6 +180,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -196,6 +198,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = false,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
@ -216,6 +219,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = false,
canAssignToCollections = true,
)
}
}
@ -231,6 +235,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
@ -251,6 +256,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -267,6 +273,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
@ -292,6 +299,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -311,7 +319,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canDelete = true, canAssignToCollections = true,
)
} returns loginState
}
@ -357,6 +365,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns loginState
}
@ -391,6 +400,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
} returns loginViewState
}
@ -436,6 +446,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns loginViewState
}
@ -484,6 +495,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
} returns loginViewState
}
@ -524,6 +536,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
@ -559,6 +572,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
} returns viewState
}
@ -598,6 +612,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
@ -640,6 +655,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
@ -691,6 +707,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
@ -725,6 +742,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns loginViewState
}
@ -743,6 +761,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
assertEquals(
@ -769,7 +788,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -832,7 +851,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -887,7 +906,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -953,7 +972,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
}
@ -978,7 +997,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
}
}
@ -994,7 +1013,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false))
}
@ -1013,7 +1032,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
organizationEventManager.trackEvent(
event = OrganizationEvent.CipherClientCopiedHiddenField(
@ -1053,7 +1072,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
}
@ -1086,7 +1105,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
}
}
@ -1120,6 +1139,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns loginViewState
}
@ -1152,7 +1172,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
organizationEventManager.trackEvent(
event = OrganizationEvent.CipherClientToggledHiddenFieldVisible(
@ -1173,7 +1193,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
}
@ -1198,7 +1218,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
}
}
@ -1218,7 +1238,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -1254,7 +1274,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -1281,7 +1301,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
}
}
@ -1305,7 +1325,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -1355,7 +1375,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
}
@ -1380,7 +1400,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
}
}
@ -1400,7 +1420,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -1433,7 +1453,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns DEFAULT_VIEW_STATE
}
@ -1458,7 +1478,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
}
}
@ -1478,7 +1498,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -1497,6 +1517,74 @@ class VaultItemViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `canAssignToCollections should be true when item is in a collection the user can manage and edit`() {
val mockCipherView = mockk<CipherView> {
every {
toViewState(
previousState = null,
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
mutableAuthCodeItemFlow.value = DataState.Loaded(data = null)
mutableCollectionsStateFlow.value = DataState.Loaded(emptyList())
verify {
mockCipherView.toViewState(
previousState = null,
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@Suppress("MaxLineLength")
@Test
fun `canAssignToCollections should be false when item is not in a collection the user can manage and edit`() {
val mockCipherView = mockk<CipherView> {
every { collectionIds } returns listOf("mockId-1", "mockId-2")
every {
toViewState(
previousState = null,
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = false,
canAssignToCollections = false,
)
} returns DEFAULT_VIEW_STATE
}
mutableVaultItemFlow.value = DataState.Loaded(data = mockCipherView)
mutableAuthCodeItemFlow.value = DataState.Loaded(data = null)
mutableCollectionsStateFlow.value = DataState.Loaded(
data = listOf(
createMockCollectionView(number = 1)
.copy(manage = false, readOnly = true),
createMockCollectionView(number = 2)
.copy(manage = true),
),
)
verify {
mockCipherView.toViewState(
previousState = null,
isPremiumUser = true,
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = false,
canAssignToCollections = false,
)
}
}
@Test
fun `on CollectionsClick should emit NavigateToCollections`() = runTest {
val viewModel = createViewModel(state = DEFAULT_STATE)
@ -1522,7 +1610,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -1580,7 +1668,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -1647,7 +1735,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = null,
canAssignToCollections = true, totpCodeItemData = null,
)
} returns loginViewState
}
@ -1827,7 +1915,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = createTotpCodeData(),
canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
)
} returns DEFAULT_VIEW_STATE
}
@ -1869,7 +1957,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = createTotpCodeData(),
canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
)
}
coVerify(exactly = 1) {
@ -1888,7 +1976,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = createTotpCodeData(),
canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
)
} returns DEFAULT_VIEW_STATE
}
@ -1916,7 +2004,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = createTotpCodeData(),
canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
)
}
}
@ -1931,7 +2019,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = createTotpCodeData(),
canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
)
} returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false))
}
@ -1951,7 +2039,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = createTotpCodeData(),
canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
)
}
}
@ -1993,7 +2081,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true,
hasMasterPassword = true,
canDelete = true,
totpCodeItemData = createTotpCodeData(),
canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
)
} returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false))
}
@ -2012,6 +2100,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2037,6 +2126,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
@ -2063,6 +2153,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2079,6 +2170,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
}
.returns(
@ -2107,6 +2199,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2124,6 +2217,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
} returns DEFAULT_VIEW_STATE
}
@ -2154,6 +2248,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2174,6 +2269,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
} returns loginViewState
}
@ -2207,6 +2303,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(),
canDelete = true,
canAssignToCollections = true,
)
organizationEventManager.trackEvent(
event = OrganizationEvent.CipherClientToggledPasswordVisible(
@ -2242,6 +2339,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns CARD_VIEW_STATE
}
@ -2269,6 +2367,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2284,6 +2383,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns createViewState(
common = DEFAULT_COMMON.copy(requiresReprompt = false),
@ -2305,6 +2405,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2321,6 +2422,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns CARD_VIEW_STATE
}
@ -2348,6 +2450,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2363,6 +2466,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns createViewState(
common = DEFAULT_COMMON.copy(requiresReprompt = false),
@ -2390,6 +2494,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2406,6 +2511,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns CARD_VIEW_STATE
}
@ -2433,6 +2539,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2448,6 +2555,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns createViewState(
common = DEFAULT_COMMON.copy(requiresReprompt = false),
@ -2469,6 +2577,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2485,6 +2594,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns CARD_VIEW_STATE
}
@ -2512,6 +2622,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2527,6 +2638,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns createViewState(
common = DEFAULT_COMMON.copy(requiresReprompt = false),
@ -2554,6 +2666,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
}
}
@ -2588,6 +2701,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns SSH_KEY_VIEW_STATE
}
@ -2646,6 +2760,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns viewState
}
@ -2685,6 +2800,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns viewState
}
@ -2725,6 +2841,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns viewState
}
@ -2762,6 +2879,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true,
totpCodeItemData = null,
canDelete = true,
canAssignToCollections = true,
)
} returns viewState
}
@ -2977,6 +3095,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
),
),
canDelete = true,
canAssignToCollections = true,
)
private val DEFAULT_VIEW_STATE: VaultItemState.ViewState.Content =

View file

@ -46,6 +46,7 @@ class CipherViewExtensionsTest {
),
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -82,6 +83,7 @@ class CipherViewExtensionsTest {
),
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -111,6 +113,7 @@ class CipherViewExtensionsTest {
),
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -140,6 +143,7 @@ class CipherViewExtensionsTest {
),
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -175,6 +179,7 @@ class CipherViewExtensionsTest {
),
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -200,6 +205,7 @@ class CipherViewExtensionsTest {
totpCodeItemData = null,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -223,6 +229,7 @@ class CipherViewExtensionsTest {
totpCodeItemData = null,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -245,6 +252,7 @@ class CipherViewExtensionsTest {
totpCodeItemData = null,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -277,6 +285,7 @@ class CipherViewExtensionsTest {
totpCodeItemData = null,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -314,6 +323,7 @@ class CipherViewExtensionsTest {
totpCodeItemData = null,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -353,6 +363,7 @@ class CipherViewExtensionsTest {
totpCodeItemData = null,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
@ -376,6 +387,7 @@ class CipherViewExtensionsTest {
totpCodeItemData = null,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
val expectedState = VaultItemState.ViewState.Content(
@ -397,6 +409,7 @@ class CipherViewExtensionsTest {
totpCodeItemData = null,
clock = fixedClock,
canDelete = true,
canAssignToCollections = true,
)
assertEquals(
VaultItemState.ViewState.Content(

View file

@ -171,6 +171,7 @@ fun createCommonContent(
requiresCloneConfirmation = false,
attachments = emptyList(),
canDelete = true,
canAssignToCollections = true,
)
} else {
VaultItemState.ViewState.Content.Common(
@ -215,6 +216,7 @@ fun createCommonContent(
),
),
canDelete = true,
canAssignToCollections = true,
)
}