BIT-509: Adding the Ability to Save a SecureNote Item (#347)

This commit is contained in:
Oleg Semenenko
2023-12-07 14:03:20 -06:00
committed by GitHub
parent deaf83e81d
commit 6498ab2ffb
6 changed files with 195 additions and 48 deletions

View File

@@ -34,11 +34,8 @@ import org.junit.Test
@Suppress("LargeClass")
class VaultAddItemScreenTest : BaseComposeTest() {
private val mutableStateFlow = MutableStateFlow(
VaultAddItemState(
selectedType = VaultAddItemState.ItemType.Login(),
),
)
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE_LOGIN)
private val viewModel = mockk<VaultAddItemViewModel>(relaxed = true) {
every { eventFlow } returns emptyFlow()
@@ -623,8 +620,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Test
fun `in ItemType_SecureNotes state changing Name text field should trigger NameTextChange`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -643,8 +639,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Test
fun `in ItemType_SecureNotes the name control should display the text provided by the state`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -665,8 +660,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Test
fun `in ItemType_SecureNotes state clicking a Folder Option should send FolderChange action`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -694,8 +688,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `in ItemType_SecureNotes the folder control should display the text provided by the state`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -717,8 +710,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `in ItemType_SecureNotes state, toggling the favorite toggle should send ToggleFavorite action`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -740,8 +732,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `in ItemType_SecureNotes the favorite toggle should be enabled or disabled according to state`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -763,8 +754,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `in ItemType_SecureNotes state, toggling the Master password re-prompt toggle should send ToggleMasterPasswordReprompt action`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -788,8 +778,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `in ItemType_SecureNotes the master password re-prompt toggle should be enabled or disabled according to state`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -811,8 +800,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `in ItemType_SecureNotes state, toggling the Master password re-prompt tooltip button should send TooltipClick action`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -831,8 +819,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Test
fun `in ItemType_SecureNotes state changing Notes text field should trigger NotesTextChange`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -853,8 +840,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `in ItemType_SecureNotes the Notes control should display the text provided by the state`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -878,8 +864,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `in ItemType_SecureNotes state clicking New Custom Field button should trigger AddNewCustomFieldClick`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -899,8 +884,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `in ItemType_SecureNotes state clicking a Ownership option should send OwnershipChange action`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -928,8 +912,7 @@ class VaultAddItemScreenTest : BaseComposeTest() {
@Suppress("MaxLineLength")
@Test
fun `in ItemType_SecureNotes the Ownership control should display the text provided by the state`() {
mutableStateFlow.value =
VaultAddItemState(selectedType = VaultAddItemState.ItemType.SecureNotes())
mutableStateFlow.value = DEFAULT_STATE_SECURE_NOTES
composeTestRule.setContent {
VaultAddItemScreen(viewModel = viewModel, onNavigateBack = {})
@@ -974,4 +957,16 @@ class VaultAddItemScreenTest : BaseComposeTest() {
}
//endregion Helper functions
companion object {
private val DEFAULT_STATE_LOGIN = VaultAddItemState(
selectedType = VaultAddItemState.ItemType.Login(),
dialog = null,
)
private val DEFAULT_STATE_SECURE_NOTES = VaultAddItemState(
selectedType = VaultAddItemState.ItemType.SecureNotes(),
dialog = null,
)
}
}

View File

@@ -2,6 +2,7 @@ package com.x8bit.bitwarden.ui.vault.feature.additem
import androidx.lifecycle.SavedStateHandle
import app.cash.turbine.test
import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.data.vault.repository.model.CreateCipherResult
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
@@ -39,7 +40,27 @@ class VaultAddItemViewModelTest : BaseViewModelTest() {
}
@Test
fun `SaveClick createCipher success should emit NavigateBack`() = runTest {
fun `SaveClick should show dialog, and remove it once an item is saved`() = runTest {
val stateWithDialog = createVaultAddLoginItemState(
dialogState = VaultAddItemState.DialogState.Loading(
R.string.saving.asText(),
),
)
val viewModel = createAddVaultItemViewModel()
coEvery {
vaultRepository.createCipher(any())
} returns CreateCipherResult.Success
viewModel.stateFlow.test {
viewModel.actionChannel.trySend(VaultAddItemAction.SaveClick)
assertEquals(initialState, awaitItem())
assertEquals(stateWithDialog, awaitItem())
assertEquals(initialState, awaitItem())
}
}
@Test
fun `SaveClick should update value to loading`() = runTest {
val viewModel = createAddVaultItemViewModel()
coEvery {
vaultRepository.createCipher(any())
@@ -505,6 +526,7 @@ class VaultAddItemViewModelTest : BaseViewModelTest() {
masterPasswordReprompt: Boolean = false,
notes: String = "",
ownership: String = "placeholder@email.com",
dialogState: VaultAddItemState.DialogState? = null,
): VaultAddItemState =
VaultAddItemState(
selectedType = VaultAddItemState.ItemType.Login(
@@ -518,6 +540,7 @@ class VaultAddItemViewModelTest : BaseViewModelTest() {
notes = notes,
ownership = ownership,
),
dialog = dialogState,
)
@Suppress("LongParameterList")
@@ -538,6 +561,7 @@ class VaultAddItemViewModelTest : BaseViewModelTest() {
notes = notes,
ownership = ownership,
),
dialog = null,
)
private fun createSavedStateHandleWithState(state: VaultAddItemState) =

View File

@@ -5,6 +5,8 @@ import com.bitwarden.core.CipherType
import com.bitwarden.core.CipherView
import com.bitwarden.core.LoginUriView
import com.bitwarden.core.LoginView
import com.bitwarden.core.SecureNoteType
import com.bitwarden.core.SecureNoteView
import com.bitwarden.core.UriMatchType
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
@@ -163,4 +165,50 @@ class VaultDataExtensionsTest {
result,
)
}
@Test
fun `toCipherView should transform SecureNotes ItemType to CipherView`() {
mockkStatic(Instant::class)
every { Instant.now() } returns Instant.MIN
val secureNotesItemType = VaultAddItemState.ItemType.SecureNotes(
name = "mockName-1",
folderName = "mockFolder-1".asText(),
favorite = false,
masterPasswordReprompt = false,
notes = "mockNotes-1",
ownership = "mockOwnership-1",
)
val result = secureNotesItemType.toCipherView()
assertEquals(
CipherView(
id = null,
organizationId = null,
folderId = null,
collectionIds = emptyList(),
key = null,
name = "mockName-1",
notes = "mockNotes-1",
type = CipherType.SECURE_NOTE,
login = null,
identity = null,
card = null,
secureNote = SecureNoteView(SecureNoteType.GENERIC),
favorite = false,
reprompt = CipherRepromptType.NONE,
organizationUseTotp = false,
edit = true,
viewPassword = true,
localData = null,
attachments = null,
fields = null,
passwordHistory = null,
creationDate = Instant.MIN,
deletedDate = null,
revisionDate = Instant.MIN,
),
result,
)
}
}