mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
BIT-526: Clone vault item (#713)
This commit is contained in:
parent
3ec95b0ffd
commit
a760127711
13 changed files with 235 additions and 30 deletions
|
@ -92,8 +92,15 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
|||
)
|
||||
vaultItemDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onNavigateToVaultEditItem = {
|
||||
navController.navigateToVaultAddEdit(VaultAddEditType.EditItem(it))
|
||||
onNavigateToVaultEditItem = { vaultItemId, isClone ->
|
||||
navController.navigateToVaultAddEdit(
|
||||
if (isClone) {
|
||||
VaultAddEditType.CloneItem(vaultItemId)
|
||||
} else {
|
||||
VaultAddEditType.EditItem(vaultItemId)
|
||||
},
|
||||
|
||||
)
|
||||
},
|
||||
onNavigateToMoveToOrganization = {
|
||||
navController.navigateToVaultMoveToOrganization(it)
|
||||
|
|
|
@ -3,11 +3,14 @@ package com.x8bit.bitwarden.ui.platform.manager.di
|
|||
import android.content.Context
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManagerImpl
|
||||
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManagerImpl
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Provides UI-based managers in the platform package.
|
||||
|
@ -15,11 +18,18 @@ import dagger.hilt.components.SingletonComponent
|
|||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class PlatformUiManagerModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideIntentManager(
|
||||
@ApplicationContext context: Context,
|
||||
): IntentManager =
|
||||
IntentManagerImpl(
|
||||
context = context,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideResourceManager(@ApplicationContext context: Context): ResourceManager =
|
||||
ResourceManagerImpl(context = context)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.x8bit.bitwarden.ui.platform.manager.resource
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
|
||||
/**
|
||||
* Interface for managing resources.
|
||||
*/
|
||||
interface ResourceManager {
|
||||
|
||||
/**
|
||||
* Method for returning a permission string from a [resId].
|
||||
*/
|
||||
fun getString(@StringRes resId: Int): String
|
||||
|
||||
/**
|
||||
* Method for returning a permission string from a [resId] with [formatArgs].
|
||||
*/
|
||||
fun getString(@StringRes resId: Int, vararg formatArgs: Any): String
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.x8bit.bitwarden.ui.platform.manager.resource
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.StringRes
|
||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||
|
||||
/**
|
||||
* Primary implementation of [ResourceManager].
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
class ResourceManagerImpl(private val context: Context) : ResourceManager {
|
||||
override fun getString(@StringRes resId: Int): String =
|
||||
context.getString(resId)
|
||||
|
||||
override fun getString(@StringRes resId: Int, vararg formatArgs: Any): String =
|
||||
context.getString(resId, *formatArgs)
|
||||
}
|
|
@ -13,6 +13,7 @@ import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
|
|||
|
||||
private const val ADD_TYPE: String = "add"
|
||||
private const val EDIT_TYPE: String = "edit"
|
||||
private const val CLONE_TYPE: String = "clone"
|
||||
private const val EDIT_ITEM_ID: String = "vault_edit_id"
|
||||
|
||||
private const val ADD_EDIT_ITEM_PREFIX: String = "vault_add_edit_item"
|
||||
|
@ -32,6 +33,7 @@ data class VaultAddEditArgs(
|
|||
vaultAddEditType = when (requireNotNull(savedStateHandle[ADD_EDIT_ITEM_TYPE])) {
|
||||
ADD_TYPE -> VaultAddEditType.AddItem
|
||||
EDIT_TYPE -> VaultAddEditType.EditItem(requireNotNull(savedStateHandle[EDIT_ITEM_ID]))
|
||||
CLONE_TYPE -> VaultAddEditType.CloneItem(requireNotNull(savedStateHandle[EDIT_ITEM_ID]))
|
||||
else -> throw IllegalStateException("Unknown VaultAddEditType.")
|
||||
},
|
||||
)
|
||||
|
@ -79,7 +81,12 @@ private fun VaultAddEditType.toTypeString(): String =
|
|||
when (this) {
|
||||
is VaultAddEditType.AddItem -> ADD_TYPE
|
||||
is VaultAddEditType.EditItem -> EDIT_TYPE
|
||||
is VaultAddEditType.CloneItem -> CLONE_TYPE
|
||||
}
|
||||
|
||||
private fun VaultAddEditType.toIdOrNull(): String? =
|
||||
(this as? VaultAddEditType.EditItem)?.vaultItemId
|
||||
when (this) {
|
||||
is VaultAddEditType.AddItem -> null
|
||||
is VaultAddEditType.CloneItem -> vaultItemId
|
||||
is VaultAddEditType.EditItem -> vaultItemId
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
|
|||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.concat
|
||||
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField
|
||||
|
@ -56,6 +57,7 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
private val clipboardManager: BitwardenClipboardManager,
|
||||
private val vaultRepository: VaultRepository,
|
||||
private val generatorRepository: GeneratorRepository,
|
||||
private val resourceManager: ResourceManager,
|
||||
) : BaseViewModel<VaultAddEditState, VaultAddEditEvent, VaultAddEditAction>(
|
||||
// We load the state from the savedStateHandle for testing purposes.
|
||||
initialState = savedStateHandle[KEY_STATE]
|
||||
|
@ -70,6 +72,7 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
)
|
||||
|
||||
is VaultAddEditType.EditItem -> VaultAddEditState.ViewState.Loading
|
||||
is VaultAddEditType.CloneItem -> VaultAddEditState.ViewState.Loading
|
||||
},
|
||||
dialog = null,
|
||||
)
|
||||
|
@ -79,18 +82,18 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
//region Initialization and Overrides
|
||||
|
||||
init {
|
||||
when (val vaultAddEditType = state.vaultAddEditType) {
|
||||
VaultAddEditType.AddItem -> Unit
|
||||
is VaultAddEditType.EditItem -> {
|
||||
state
|
||||
.vaultAddEditType
|
||||
.vaultItemId
|
||||
?.let { itemId ->
|
||||
vaultRepository
|
||||
.getVaultItemStateFlow(vaultAddEditType.vaultItemId)
|
||||
.getVaultItemStateFlow(itemId)
|
||||
// We'll stop getting updates as soon as we get some loaded data.
|
||||
.takeUntilLoaded()
|
||||
.map { VaultAddEditAction.Internal.VaultDataReceive(it) }
|
||||
.onEach(::sendAction)
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
}
|
||||
|
||||
vaultRepository
|
||||
.totpCodeFlow
|
||||
|
@ -240,6 +243,11 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
)
|
||||
sendAction(VaultAddEditAction.Internal.UpdateCipherResultReceive(result))
|
||||
}
|
||||
|
||||
is VaultAddEditType.CloneItem -> {
|
||||
val result = vaultRepository.createCipher(cipherView = content.toCipherView())
|
||||
sendAction(VaultAddEditAction.Internal.CreateCipherResultReceive(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -810,6 +818,7 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private fun handleVaultDataReceive(action: VaultAddEditAction.Internal.VaultDataReceive) {
|
||||
when (val vaultDataState = action.vaultDataState) {
|
||||
is DataState.Error -> {
|
||||
|
@ -827,7 +836,10 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
it.copy(
|
||||
viewState = vaultDataState
|
||||
.data
|
||||
?.toViewState()
|
||||
?.toViewState(
|
||||
isClone = it.isCloneMode,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
?: VaultAddEditState.ViewState.Error(
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
|
@ -858,7 +870,10 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
it.copy(
|
||||
viewState = vaultDataState
|
||||
.data
|
||||
?.toViewState()
|
||||
?.toViewState(
|
||||
isClone = it.isCloneMode,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
?: VaultAddEditState.ViewState.Error(
|
||||
message = R.string.generic_error_message.asText(),
|
||||
),
|
||||
|
@ -1041,6 +1056,7 @@ data class VaultAddEditState(
|
|||
get() = when (vaultAddEditType) {
|
||||
VaultAddEditType.AddItem -> R.string.add_item.asText()
|
||||
is VaultAddEditType.EditItem -> R.string.edit_item.asText()
|
||||
is VaultAddEditType.CloneItem -> R.string.add_item.asText()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1048,6 +1064,11 @@ data class VaultAddEditState(
|
|||
*/
|
||||
val isAddItemMode: Boolean get() = vaultAddEditType == VaultAddEditType.AddItem
|
||||
|
||||
/**
|
||||
* Helper to determine if the UI should display the content in clone mode.
|
||||
*/
|
||||
val isCloneMode: Boolean get() = vaultAddEditType is VaultAddEditType.CloneItem
|
||||
|
||||
/**
|
||||
* Enum representing the main type options for the vault, such as LOGIN, CARD, etc.
|
||||
*
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.bitwarden.core.FieldType
|
|||
import com.bitwarden.core.FieldView
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth
|
||||
|
@ -18,7 +19,10 @@ import java.util.UUID
|
|||
/**
|
||||
* Transforms [CipherView] into [VaultAddEditState.ViewState].
|
||||
*/
|
||||
fun CipherView.toViewState(): VaultAddEditState.ViewState =
|
||||
fun CipherView.toViewState(
|
||||
isClone: Boolean,
|
||||
resourceManager: ResourceManager,
|
||||
): VaultAddEditState.ViewState =
|
||||
VaultAddEditState.ViewState.Content(
|
||||
type = when (type) {
|
||||
CipherType.LOGIN -> {
|
||||
|
@ -63,7 +67,10 @@ fun CipherView.toViewState(): VaultAddEditState.ViewState =
|
|||
},
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
originalCipher = this,
|
||||
name = this.name,
|
||||
name = name.appendCloneTextIfRequired(
|
||||
isClone = isClone,
|
||||
resourceManager = resourceManager,
|
||||
),
|
||||
favorite = this.favorite,
|
||||
masterPasswordReprompt = this.reprompt == CipherRepromptType.PASSWORD,
|
||||
notes = this.notes.orEmpty(),
|
||||
|
@ -121,3 +128,13 @@ private fun String?.toExpirationMonthOrDefault(): VaultCardExpirationMonth =
|
|||
.entries
|
||||
.find { it.number == this }
|
||||
?: VaultCardExpirationMonth.SELECT
|
||||
|
||||
private fun String.appendCloneTextIfRequired(
|
||||
isClone: Boolean,
|
||||
resourceManager: ResourceManager,
|
||||
): String =
|
||||
if (isClone) {
|
||||
plus(" - ${resourceManager.getString(R.string.clone)}")
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ data class VaultItemArgs(val vaultItemId: String) {
|
|||
*/
|
||||
fun NavGraphBuilder.vaultItemDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToVaultEditItem: (vaultItemId: String) -> Unit,
|
||||
onNavigateToVaultEditItem: (vaultItemId: String, isClone: Boolean) -> Unit,
|
||||
onNavigateToMoveToOrganization: (vaultItemId: String) -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
|
|
|
@ -59,7 +59,7 @@ fun VaultItemScreen(
|
|||
viewModel: VaultItemViewModel = hiltViewModel(),
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToVaultAddEditItem: (vaultItemId: String) -> Unit,
|
||||
onNavigateToVaultAddEditItem: (vaultItemId: String, isClone: Boolean) -> Unit,
|
||||
onNavigateToMoveToOrganization: (vaultItemId: String) -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
|
@ -75,8 +75,7 @@ fun VaultItemScreen(
|
|||
VaultItemEvent.NavigateBack -> onNavigateBack()
|
||||
|
||||
is VaultItemEvent.NavigateToAddEdit -> {
|
||||
// TODO Implement cloning in BIT-526
|
||||
onNavigateToVaultAddEditItem(event.itemId)
|
||||
onNavigateToVaultAddEditItem(event.itemId, event.isClone)
|
||||
}
|
||||
|
||||
is VaultItemEvent.NavigateToPasswordHistory -> {
|
||||
|
|
|
@ -7,19 +7,34 @@ import kotlinx.parcelize.Parcelize
|
|||
* Represents the difference between create a completely new cipher and editing an existing one.
|
||||
*/
|
||||
sealed class VaultAddEditType : Parcelable {
|
||||
|
||||
/**
|
||||
* The ID of the vault item (nullable).
|
||||
*/
|
||||
abstract val vaultItemId: String?
|
||||
|
||||
/**
|
||||
* Indicates that we want to create a completely new vault item.
|
||||
*/
|
||||
@Parcelize
|
||||
data object AddItem : VaultAddEditType()
|
||||
data object AddItem : VaultAddEditType() {
|
||||
override val vaultItemId: String?
|
||||
get() = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that we want to edit an existing item.
|
||||
*
|
||||
* @param vaultItemId The ID of the vault item to edit.
|
||||
*/
|
||||
@Parcelize
|
||||
data class EditItem(
|
||||
val vaultItemId: String,
|
||||
override val vaultItemId: String,
|
||||
) : VaultAddEditType()
|
||||
|
||||
/**
|
||||
* Indicates that we want to clone an existing item.
|
||||
*/
|
||||
@Parcelize
|
||||
data class CloneItem(
|
||||
override val vaultItemId: String,
|
||||
) : VaultAddEditType()
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.UpdateCipherResult
|
|||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.Text
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField
|
||||
|
@ -58,7 +59,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
private val totpTestCodeFlow: MutableSharedFlow<TotpCodeResult> = bufferedMutableSharedFlow()
|
||||
|
||||
private val mutableVaultItemFlow = MutableStateFlow<DataState<CipherView?>>(DataState.Loading)
|
||||
|
||||
private val resourceManager: ResourceManager = mockk()
|
||||
private val clipboardManager: BitwardenClipboardManager = mockk()
|
||||
private val vaultRepository: VaultRepository = mockk {
|
||||
every { getVaultItemStateFlow(DEFAULT_EDIT_ITEM_ID) } returns mutableVaultItemFlow
|
||||
|
@ -131,6 +132,25 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial clone state should be correct`() = runTest {
|
||||
val vaultAddEditType = VaultAddEditType.CloneItem(DEFAULT_EDIT_ITEM_ID)
|
||||
val initState = createVaultAddItemState(vaultAddEditType = vaultAddEditType)
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
savedStateHandle = createSavedStateHandleWithState(
|
||||
state = initState,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
initState.copy(viewState = VaultAddEditState.ViewState.Loading),
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
verify(exactly = 1) {
|
||||
vaultRepository.getVaultItemStateFlow(DEFAULT_EDIT_ITEM_ID)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `CloseClick should emit NavigateBack`() = runTest {
|
||||
val viewModel = createAddVaultItemViewModel()
|
||||
|
@ -254,7 +274,12 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
name = "mockName",
|
||||
),
|
||||
)
|
||||
every { cipherView.toViewState() } returns stateWithName.viewState
|
||||
every {
|
||||
cipherView.toViewState(
|
||||
isClone = false,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
} returns stateWithName.viewState
|
||||
mutableVaultItemFlow.value = DataState.Loaded(cipherView)
|
||||
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
|
@ -276,7 +301,10 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
|
||||
coVerify(exactly = 1) {
|
||||
cipherView.toViewState()
|
||||
cipherView.toViewState(
|
||||
isClone = false,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
vaultRepository.updateCipher(DEFAULT_EDIT_ITEM_ID, any())
|
||||
}
|
||||
}
|
||||
|
@ -294,7 +322,12 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
),
|
||||
)
|
||||
|
||||
every { cipherView.toViewState() } returns stateWithName.viewState
|
||||
every {
|
||||
cipherView.toViewState(
|
||||
isClone = false,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
} returns stateWithName.viewState
|
||||
coEvery {
|
||||
vaultRepository.updateCipher(DEFAULT_EDIT_ITEM_ID, any())
|
||||
} returns UpdateCipherResult.Error(errorMessage = null)
|
||||
|
@ -336,7 +369,12 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
val errorMessage = "You do not have permission to edit this."
|
||||
|
||||
every { cipherView.toViewState() } returns stateWithName.viewState
|
||||
every {
|
||||
cipherView.toViewState(
|
||||
isClone = false,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
} returns stateWithName.viewState
|
||||
coEvery {
|
||||
vaultRepository.updateCipher(DEFAULT_EDIT_ITEM_ID, any())
|
||||
} returns UpdateCipherResult.Error(errorMessage = errorMessage)
|
||||
|
@ -1192,6 +1230,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
clipboardManager = clipboardManager,
|
||||
vaultRepository = vaultRepository,
|
||||
generatorRepository = generatorRepository,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1484,6 +1523,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
when (vaultAddEditType) {
|
||||
VaultAddEditType.AddItem -> "add"
|
||||
is VaultAddEditType.EditItem -> "edit"
|
||||
is VaultAddEditType.CloneItem -> "clone"
|
||||
},
|
||||
)
|
||||
set("vault_edit_id", (vaultAddEditType as? VaultAddEditType.EditItem)?.vaultItemId)
|
||||
|
@ -1494,12 +1534,14 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
bitwardenClipboardManager: BitwardenClipboardManager = clipboardManager,
|
||||
vaultRepo: VaultRepository = vaultRepository,
|
||||
generatorRepo: GeneratorRepository = generatorRepository,
|
||||
bitwardenResourceManager: ResourceManager = resourceManager,
|
||||
): VaultAddEditViewModel =
|
||||
VaultAddEditViewModel(
|
||||
savedStateHandle = savedStateHandle,
|
||||
clipboardManager = bitwardenClipboardManager,
|
||||
vaultRepository = vaultRepo,
|
||||
generatorRepository = generatorRepo,
|
||||
resourceManager = bitwardenResourceManager,
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,10 +14,12 @@ import com.bitwarden.core.SecureNoteType
|
|||
import com.bitwarden.core.SecureNoteView
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.manager.resource.ResourceManager
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultLinkedFieldType
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
|
@ -29,6 +31,10 @@ import java.util.UUID
|
|||
|
||||
class CipherViewExtensionsTest {
|
||||
|
||||
private val resourceManager: ResourceManager = mockk {
|
||||
every { getString(R.string.clone) } returns "Clone"
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mockkStatic(UUID::randomUUID)
|
||||
|
@ -44,7 +50,10 @@ class CipherViewExtensionsTest {
|
|||
fun `toViewState should create a Card ViewState`() {
|
||||
val cipherView = DEFAULT_CARD_CIPHER_VIEW
|
||||
|
||||
val result = cipherView.toViewState()
|
||||
val result = cipherView.toViewState(
|
||||
isClone = false,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
VaultAddEditState.ViewState.Content(
|
||||
|
@ -85,7 +94,10 @@ class CipherViewExtensionsTest {
|
|||
fun `toViewState should create a Identity ViewState`() {
|
||||
val cipherView = DEFAULT_IDENTITY_CIPHER_VIEW
|
||||
|
||||
val result = cipherView.toViewState()
|
||||
val result = cipherView.toViewState(
|
||||
isClone = false,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
VaultAddEditState.ViewState.Content(
|
||||
|
@ -131,7 +143,10 @@ class CipherViewExtensionsTest {
|
|||
fun `toViewState should create a Login ViewState`() {
|
||||
val cipherView = DEFAULT_LOGIN_CIPHER_VIEW
|
||||
|
||||
val result = cipherView.toViewState()
|
||||
val result = cipherView.toViewState(
|
||||
isClone = false,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
VaultAddEditState.ViewState.Content(
|
||||
|
@ -172,7 +187,10 @@ class CipherViewExtensionsTest {
|
|||
fun `toViewState should create a Secure Notes ViewState`() {
|
||||
val cipherView = DEFAULT_SECURE_NOTES_CIPHER_VIEW
|
||||
|
||||
val result = cipherView.toViewState()
|
||||
val result = cipherView.toViewState(
|
||||
isClone = false,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
VaultAddEditState.ViewState.Content(
|
||||
|
@ -197,6 +215,39 @@ class CipherViewExtensionsTest {
|
|||
result,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toViewState with isClone true should append clone text to the cipher name`() {
|
||||
val cipherView = DEFAULT_SECURE_NOTES_CIPHER_VIEW
|
||||
|
||||
val result = cipherView.toViewState(
|
||||
isClone = true,
|
||||
resourceManager = resourceManager,
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
originalCipher = cipherView,
|
||||
name = "cipher - Clone",
|
||||
folderName = R.string.folder_none.asText(),
|
||||
favorite = false,
|
||||
masterPasswordReprompt = true,
|
||||
notes = "Lots of notes",
|
||||
ownership = "",
|
||||
customFieldData = listOf(
|
||||
VaultAddEditState.Custom.BooleanField(TEST_ID, "TestBoolean", false),
|
||||
VaultAddEditState.Custom.TextField(TEST_ID, "TestText", "TestText"),
|
||||
VaultAddEditState.Custom.HiddenField(TEST_ID, "TestHidden", "TestHidden"),
|
||||
),
|
||||
availableFolders = emptyList(),
|
||||
availableOwners = emptyList(),
|
||||
),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.SecureNotes,
|
||||
),
|
||||
result,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_BASE_CIPHER_VIEW: CipherView = CipherView(
|
||||
|
|
|
@ -70,7 +70,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
|||
VaultItemScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { onNavigateBackCalled = true },
|
||||
onNavigateToVaultAddEditItem = { onNavigateToVaultEditItemId = it },
|
||||
onNavigateToVaultAddEditItem = { id, _ -> onNavigateToVaultEditItemId = id },
|
||||
onNavigateToMoveToOrganization = { onNavigateToMoveToOrganizationItemId = it },
|
||||
intentManager = intentManager,
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue