Merge branch 'main' into main

This commit is contained in:
SymphonicDeviation 2024-11-07 19:06:02 -05:00 committed by GitHub
commit 6d06fd3c2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 565 additions and 43 deletions

View file

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

View file

@ -1591,16 +1591,29 @@ class VaultAddEditViewModel @Inject constructor(
currentAccount = userData?.activeAccount, currentAccount = userData?.activeAccount,
vaultAddEditType = vaultAddEditType, vaultAddEditType = vaultAddEditType,
) { currentAccount, cipherView -> ) { 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 val canDelete = vaultData.collectionViewList
.none { .none {
val itemIsInCollection = cipherView val isItemInCollection = cipherView
?.collectionIds ?.collectionIds
?.contains(it.id) == true ?.contains(it.id) == true
val canManageCollection = it.manage isItemInCollection && !it.manage
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 = 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 // Derive the view state from the current Cipher for Edit mode
// or use the current state for Add // or use the current state for Add
(cipherView (cipherView
@ -1611,6 +1624,7 @@ class VaultAddEditViewModel @Inject constructor(
resourceManager = resourceManager, resourceManager = resourceManager,
clock = clock, clock = clock,
canDelete = canDelete, canDelete = canDelete,
canAssignToCollections = canAssignToCollections,
) )
?: viewState) ?: viewState)
.appendFolderAndOwnerData( .appendFolderAndOwnerData(
@ -2048,6 +2062,12 @@ data class VaultAddEditState(
?.canDelete ?.canDelete
?: false ?: 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. * Enum representing the main type options for the vault, such as LOGIN, CARD, etc.
* *
@ -2125,6 +2145,7 @@ data class VaultAddEditState(
val availableOwners: List<Owner> = emptyList(), val availableOwners: List<Owner> = emptyList(),
val hasOrganizations: Boolean = false, val hasOrganizations: Boolean = false,
val canDelete: Boolean = true, val canDelete: Boolean = true,
val canAssignToCollections: Boolean = true,
) : Parcelable { ) : Parcelable {
/** /**

View file

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

View file

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

View file

@ -111,9 +111,19 @@ class VaultItemViewModel @Inject constructor(
?.collectionIds ?.collectionIds
?.contains(it.id) == true ?.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 ?: true
@ -121,6 +131,7 @@ class VaultItemViewModel @Inject constructor(
cipher = cipherViewState.data, cipher = cipherViewState.data,
totpCodeItemData = totpCodeData, totpCodeItemData = totpCodeData,
canDelete = canDelete, canDelete = canDelete,
canAssociateToCollections = canAssignToCollections,
) )
}, },
) )
@ -933,6 +944,7 @@ class VaultItemViewModel @Inject constructor(
hasMasterPassword = account.hasMasterPassword, hasMasterPassword = account.hasMasterPassword,
totpCodeItemData = this.data?.totpCodeItemData, totpCodeItemData = this.data?.totpCodeItemData,
canDelete = this.data?.canDelete == true, canDelete = this.data?.canDelete == true,
canAssignToCollections = this.data?.canAssociateToCollections == true,
) )
?: VaultItemState.ViewState.Error(message = errorText) ?: VaultItemState.ViewState.Error(message = errorText)
@ -1179,6 +1191,12 @@ data class VaultItemState(
?.common ?.common
?.canDelete == true ?.canDelete == true
val canAssignToCollections: Boolean
get() = viewState.asContentOrNull()
?.common
?.canAssignToCollections
?: false
/** /**
* The text to display on the deletion confirmation dialog. * The text to display on the deletion confirmation dialog.
*/ */
@ -1230,6 +1248,10 @@ data class VaultItemState(
* @property requiresCloneConfirmation Indicates user confirmation is required when * @property requiresCloneConfirmation Indicates user confirmation is required when
* cloning a cipher. * cloning a cipher.
* @property currentCipher The cipher that is currently being viewed (nullable). * @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 @Parcelize
data class Common( data class Common(
@ -1243,6 +1265,7 @@ data class VaultItemState(
val currentCipher: CipherView? = null, val currentCipher: CipherView? = null,
val attachments: List<AttachmentItem>?, val attachments: List<AttachmentItem>?,
val canDelete: Boolean, val canDelete: Boolean,
val canAssignToCollections: Boolean,
) : Parcelable { ) : Parcelable {
/** /**

View file

@ -7,9 +7,12 @@ import com.bitwarden.vault.CipherView
* *
* @property cipher The cipher view for the item. * @property cipher The cipher view for the item.
* @property totpCodeItemData The data for the totp code. * @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( data class VaultItemStateData(
val cipher: CipherView?, val cipher: CipherView?,
val totpCodeItemData: TotpCodeItemData?, val totpCodeItemData: TotpCodeItemData?,
val canDelete: Boolean, val canDelete: Boolean,
val canAssociateToCollections: Boolean,
) )

View file

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

View file

@ -8,6 +8,7 @@ import com.bitwarden.vault.CollectionView
fun createMockCollectionView( fun createMockCollectionView(
number: Int, number: Int,
name: String? = null, name: String? = null,
readOnly: Boolean = false,
manage: Boolean = true, manage: Boolean = true,
): CollectionView = ): CollectionView =
CollectionView( CollectionView(
@ -16,6 +17,6 @@ fun createMockCollectionView(
hidePasswords = false, hidePasswords = false,
name = name ?: "mockName-$number", name = name ?: "mockName-$number",
externalId = "mockExternalId-$number", externalId = "mockExternalId-$number",
readOnly = false, readOnly = readOnly,
manage = manage, 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.assertTextContains
import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.click import androidx.compose.ui.test.click
import androidx.compose.ui.test.filter
import androidx.compose.ui.test.filterToOne import androidx.compose.ui.test.filterToOne
import androidx.compose.ui.test.hasAnyAncestor import androidx.compose.ui.test.hasAnyAncestor
import androidx.compose.ui.test.hasContentDescription import androidx.compose.ui.test.hasContentDescription
@ -3049,6 +3050,57 @@ class VaultAddEditScreenTest : BaseComposeTest() {
.assertIsDisplayed() .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 @Test
fun `Menu should display correct items when cipher is not in a collection`() { fun `Menu should display correct items when cipher is not in a collection`() {
mutableStateFlow.update { mutableStateFlow.update {

View file

@ -1227,6 +1227,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = false, canDelete = false,
canAssignToCollections = true,
) )
} returns stateWithName.viewState } returns stateWithName.viewState
@ -1257,6 +1258,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = false, canDelete = false,
canAssignToCollections = false,
) )
} }
} }
@ -1281,6 +1283,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
), ),
notes = "mockNotes-1", notes = "mockNotes-1",
canDelete = true, canDelete = true,
canAssociateToCollections = true,
), ),
) )
@ -1292,6 +1295,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns stateWithName.viewState } returns stateWithName.viewState
@ -1302,6 +1306,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
createMockCollectionView( createMockCollectionView(
number = 1, number = 1,
manage = true, manage = true,
readOnly = false,
), ),
), ),
), ),
@ -1322,6 +1327,214 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = true, 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, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns stateWithName.viewState } returns stateWithName.viewState
mutableVaultDataFlow.value = DataState.Loaded( mutableVaultDataFlow.value = DataState.Loaded(
@ -1474,6 +1688,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
vaultRepository.updateCipher(DEFAULT_EDIT_ITEM_ID, any()) vaultRepository.updateCipher(DEFAULT_EDIT_ITEM_ID, any())
} }
@ -1509,6 +1724,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns stateWithName.viewState } returns stateWithName.viewState
coEvery { coEvery {
@ -1572,6 +1788,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns stateWithName.viewState } returns stateWithName.viewState
coEvery { coEvery {
@ -1638,6 +1855,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns stateWithName.viewState } returns stateWithName.viewState
mutableVaultDataFlow.value = DataState.Loaded( mutableVaultDataFlow.value = DataState.Loaded(
@ -1695,6 +1913,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns stateWithName.viewState } returns stateWithName.viewState
every { fido2CredentialManager.isUserVerified } returns true every { fido2CredentialManager.isUserVerified } returns true
@ -1757,6 +1976,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = fixedClock, clock = fixedClock,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns stateWithName.viewState } returns stateWithName.viewState
every { fido2CredentialManager.isUserVerified } returns false every { fido2CredentialManager.isUserVerified } returns false
@ -4121,6 +4341,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
selectedOwnerId: String? = null, selectedOwnerId: String? = null,
hasOrganizations: Boolean = true, hasOrganizations: Boolean = true,
canDelete: Boolean = true, canDelete: Boolean = true,
canAssociateToCollections: Boolean = true,
): VaultAddEditState.ViewState.Content.Common = ): VaultAddEditState.ViewState.Content.Common =
VaultAddEditState.ViewState.Content.Common( VaultAddEditState.ViewState.Content.Common(
name = name, name = name,
@ -4135,6 +4356,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
availableOwners = availableOwners, availableOwners = availableOwners,
hasOrganizations = hasOrganizations, hasOrganizations = hasOrganizations,
canDelete = canDelete, canDelete = canDelete,
canAssignToCollections = canAssociateToCollections,
) )
@Suppress("LongParameterList") @Suppress("LongParameterList")

View file

@ -76,6 +76,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = FIXED_CLOCK, clock = FIXED_CLOCK,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
assertEquals( assertEquals(
@ -123,6 +124,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = FIXED_CLOCK, clock = FIXED_CLOCK,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
assertEquals( assertEquals(
@ -175,6 +177,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = FIXED_CLOCK, clock = FIXED_CLOCK,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
assertEquals( assertEquals(
@ -236,6 +239,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = FIXED_CLOCK, clock = FIXED_CLOCK,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
assertEquals( assertEquals(
@ -294,6 +298,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = FIXED_CLOCK, clock = FIXED_CLOCK,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
assertEquals( assertEquals(
@ -330,6 +335,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = FIXED_CLOCK, clock = FIXED_CLOCK,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
assertEquals( assertEquals(
@ -375,6 +381,7 @@ class CipherViewExtensionsTest {
resourceManager = resourceManager, resourceManager = resourceManager,
clock = FIXED_CLOCK, clock = FIXED_CLOCK,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
assertEquals( 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 @Test
fun `Collections menu click should send CollectionsClick action`() { fun `Collections menu click should send CollectionsClick action`() {
mutableStateFlow.update { mutableStateFlow.update {
@ -2393,6 +2437,7 @@ private val DEFAULT_COMMON: VaultItemState.ViewState.Content.Common =
), ),
), ),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
private val DEFAULT_PASSKEY = R.string.created_xy.asText( private val DEFAULT_PASSKEY = R.string.created_xy.asText(
@ -2475,6 +2520,7 @@ private val EMPTY_COMMON: VaultItemState.ViewState.Content.Common =
requiresCloneConfirmation = false, requiresCloneConfirmation = false,
attachments = emptyList(), attachments = emptyList(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
private val EMPTY_LOGIN_TYPE: VaultItemState.ViewState.Content.ItemType.Login = private val EMPTY_LOGIN_TYPE: VaultItemState.ViewState.Content.ItemType.Login =

View file

@ -166,6 +166,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -179,6 +180,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -196,6 +198,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = false, canDelete = false,
canAssignToCollections = true,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -216,6 +219,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = false, canDelete = false,
canAssignToCollections = true,
) )
} }
} }
@ -231,6 +235,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -251,6 +256,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -267,6 +273,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -292,6 +299,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -311,7 +319,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true, canAssignToCollections = true,
) )
} returns loginState } returns loginState
} }
@ -357,6 +365,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns loginState } returns loginState
} }
@ -391,6 +400,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns loginViewState } returns loginViewState
} }
@ -436,6 +446,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns loginViewState } returns loginViewState
} }
@ -484,6 +495,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns loginViewState } returns loginViewState
} }
@ -524,6 +536,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -559,6 +572,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns viewState } returns viewState
} }
@ -598,6 +612,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -640,6 +655,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -691,6 +707,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -725,6 +742,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns loginViewState } returns loginViewState
} }
@ -743,6 +761,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
assertEquals( assertEquals(
@ -769,7 +788,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } returns loginViewState
} }
@ -832,7 +851,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } returns loginViewState
} }
@ -887,7 +906,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } returns loginViewState
} }
@ -953,7 +972,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -978,7 +997,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} }
} }
@ -994,7 +1013,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false)) } returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false))
} }
@ -1013,7 +1032,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
organizationEventManager.trackEvent( organizationEventManager.trackEvent(
event = OrganizationEvent.CipherClientCopiedHiddenField( event = OrganizationEvent.CipherClientCopiedHiddenField(
@ -1053,7 +1072,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -1086,7 +1105,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} }
} }
@ -1120,6 +1139,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns loginViewState } returns loginViewState
} }
@ -1152,7 +1172,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
organizationEventManager.trackEvent( organizationEventManager.trackEvent(
event = OrganizationEvent.CipherClientToggledHiddenFieldVisible( event = OrganizationEvent.CipherClientToggledHiddenFieldVisible(
@ -1173,7 +1193,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -1198,7 +1218,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} }
} }
@ -1218,7 +1238,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } returns loginViewState
} }
@ -1254,7 +1274,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } returns loginViewState
} }
@ -1281,7 +1301,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} }
} }
@ -1305,7 +1325,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } returns loginViewState
} }
@ -1355,7 +1375,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -1380,7 +1400,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} }
} }
@ -1400,7 +1420,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } returns loginViewState
} }
@ -1433,7 +1453,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -1458,7 +1478,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} }
} }
@ -1478,7 +1498,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } 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 @Test
fun `on CollectionsClick should emit NavigateToCollections`() = runTest { fun `on CollectionsClick should emit NavigateToCollections`() = runTest {
val viewModel = createViewModel(state = DEFAULT_STATE) val viewModel = createViewModel(state = DEFAULT_STATE)
@ -1522,7 +1610,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } returns loginViewState
} }
@ -1580,7 +1668,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } returns loginViewState
} }
@ -1647,7 +1735,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = null, canAssignToCollections = true, totpCodeItemData = null,
) )
} returns loginViewState } returns loginViewState
} }
@ -1827,7 +1915,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = createTotpCodeData(), canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -1869,7 +1957,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = createTotpCodeData(), canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
) )
} }
coVerify(exactly = 1) { coVerify(exactly = 1) {
@ -1888,7 +1976,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = createTotpCodeData(), canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -1916,7 +2004,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = createTotpCodeData(), canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
) )
} }
} }
@ -1931,7 +2019,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = createTotpCodeData(), canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
) )
} returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false)) } returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false))
} }
@ -1951,7 +2039,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = createTotpCodeData(), canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
) )
} }
} }
@ -1993,7 +2081,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
isPremiumUser = true, isPremiumUser = true,
hasMasterPassword = true, hasMasterPassword = true,
canDelete = true, canDelete = true,
totpCodeItemData = createTotpCodeData(), canAssignToCollections = true, totpCodeItemData = createTotpCodeData(),
) )
} returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false)) } returns createViewState(common = DEFAULT_COMMON.copy(requiresReprompt = false))
} }
@ -2012,6 +2100,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2037,6 +2126,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -2063,6 +2153,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2079,6 +2170,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
.returns( .returns(
@ -2107,6 +2199,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2124,6 +2217,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns DEFAULT_VIEW_STATE } returns DEFAULT_VIEW_STATE
} }
@ -2154,6 +2248,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2174,6 +2269,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns loginViewState } returns loginViewState
} }
@ -2207,6 +2303,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = createTotpCodeData(), totpCodeItemData = createTotpCodeData(),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
organizationEventManager.trackEvent( organizationEventManager.trackEvent(
event = OrganizationEvent.CipherClientToggledPasswordVisible( event = OrganizationEvent.CipherClientToggledPasswordVisible(
@ -2242,6 +2339,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns CARD_VIEW_STATE } returns CARD_VIEW_STATE
} }
@ -2269,6 +2367,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2284,6 +2383,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns createViewState( } returns createViewState(
common = DEFAULT_COMMON.copy(requiresReprompt = false), common = DEFAULT_COMMON.copy(requiresReprompt = false),
@ -2305,6 +2405,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2321,6 +2422,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns CARD_VIEW_STATE } returns CARD_VIEW_STATE
} }
@ -2348,6 +2450,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2363,6 +2466,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns createViewState( } returns createViewState(
common = DEFAULT_COMMON.copy(requiresReprompt = false), common = DEFAULT_COMMON.copy(requiresReprompt = false),
@ -2390,6 +2494,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2406,6 +2511,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns CARD_VIEW_STATE } returns CARD_VIEW_STATE
} }
@ -2433,6 +2539,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2448,6 +2555,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns createViewState( } returns createViewState(
common = DEFAULT_COMMON.copy(requiresReprompt = false), common = DEFAULT_COMMON.copy(requiresReprompt = false),
@ -2469,6 +2577,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2485,6 +2594,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns CARD_VIEW_STATE } returns CARD_VIEW_STATE
} }
@ -2512,6 +2622,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2527,6 +2638,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns createViewState( } returns createViewState(
common = DEFAULT_COMMON.copy(requiresReprompt = false), common = DEFAULT_COMMON.copy(requiresReprompt = false),
@ -2554,6 +2666,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} }
} }
@ -2583,6 +2696,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns SSH_KEY_VIEW_STATE } returns SSH_KEY_VIEW_STATE
} }
@ -2613,6 +2727,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns SSH_KEY_VIEW_STATE } returns SSH_KEY_VIEW_STATE
} }
@ -2650,6 +2765,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns SSH_KEY_VIEW_STATE } returns SSH_KEY_VIEW_STATE
} }
@ -2696,6 +2812,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns viewState } returns viewState
} }
@ -2735,6 +2852,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns viewState } returns viewState
} }
@ -2775,6 +2893,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns viewState } returns viewState
} }
@ -2812,6 +2931,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
hasMasterPassword = true, hasMasterPassword = true,
totpCodeItemData = null, totpCodeItemData = null,
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
} returns viewState } returns viewState
} }
@ -3027,6 +3147,7 @@ class VaultItemViewModelTest : BaseViewModelTest() {
), ),
), ),
canDelete = true, canDelete = true,
canAssignToCollections = true,
) )
private val DEFAULT_VIEW_STATE: VaultItemState.ViewState.Content = private val DEFAULT_VIEW_STATE: VaultItemState.ViewState.Content =

View file

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

View file

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