BIT-1642: Pre-populate the add item screen URI during autofill (#863)

This commit is contained in:
Brian Yencho 2024-01-29 22:27:25 -06:00 committed by Álison Fernandes
parent 84a983e755
commit 1e64c82e83
4 changed files with 136 additions and 7 deletions

View file

@ -7,7 +7,10 @@ import com.bitwarden.core.CipherView
import com.x8bit.bitwarden.R import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.model.DataState
import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
@ -27,6 +30,7 @@ import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toDefaultAddTypeContent
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState
import com.x8bit.bitwarden.ui.vault.feature.vault.util.toCipherView import com.x8bit.bitwarden.ui.vault.feature.vault.util.toCipherView
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
@ -59,27 +63,41 @@ private const val KEY_STATE = "state"
* @param savedStateHandle Handles the navigation arguments of this ViewModel. * @param savedStateHandle Handles the navigation arguments of this ViewModel.
*/ */
@HiltViewModel @HiltViewModel
@Suppress("TooManyFunctions", "LargeClass") @Suppress("TooManyFunctions", "LargeClass", "LongParameterList")
class VaultAddEditViewModel @Inject constructor( class VaultAddEditViewModel @Inject constructor(
savedStateHandle: SavedStateHandle, savedStateHandle: SavedStateHandle,
private val authRepository: AuthRepository, private val authRepository: AuthRepository,
private val clipboardManager: BitwardenClipboardManager, private val clipboardManager: BitwardenClipboardManager,
private val vaultRepository: VaultRepository, private val vaultRepository: VaultRepository,
private val generatorRepository: GeneratorRepository, private val generatorRepository: GeneratorRepository,
private val specialCircumstanceManager: SpecialCircumstanceManager,
private val resourceManager: ResourceManager, private val resourceManager: ResourceManager,
) : BaseViewModel<VaultAddEditState, VaultAddEditEvent, VaultAddEditAction>( ) : BaseViewModel<VaultAddEditState, VaultAddEditEvent, VaultAddEditAction>(
// We load the state from the savedStateHandle for testing purposes. // We load the state from the savedStateHandle for testing purposes.
initialState = savedStateHandle[KEY_STATE] initialState = savedStateHandle[KEY_STATE]
?: run { ?: run {
val vaultAddEditType = VaultAddEditArgs(savedStateHandle).vaultAddEditType val vaultAddEditType = VaultAddEditArgs(savedStateHandle).vaultAddEditType
// Check for autofill data to pre-populate
val autofillSelectionData: AutofillSelectionData? =
when (val specialCircumstance = specialCircumstanceManager.specialCircumstance) {
is SpecialCircumstance.AutofillSelection -> {
specialCircumstance.autofillSelectionData
}
else -> null
}
val defaultAddTypeContent = autofillSelectionData
?.toDefaultAddTypeContent()
?: VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(),
type = VaultAddEditState.ViewState.Content.ItemType.Login(),
)
VaultAddEditState( VaultAddEditState(
vaultAddEditType = vaultAddEditType, vaultAddEditType = vaultAddEditType,
viewState = when (vaultAddEditType) { viewState = when (vaultAddEditType) {
VaultAddEditType.AddItem -> VaultAddEditState.ViewState.Content( VaultAddEditType.AddItem -> defaultAddTypeContent
common = VaultAddEditState.ViewState.Content.Common(),
type = VaultAddEditState.ViewState.Content.ItemType.Login(),
)
is VaultAddEditType.EditItem -> VaultAddEditState.ViewState.Loading is VaultAddEditType.EditItem -> VaultAddEditState.ViewState.Loading
is VaultAddEditType.CloneItem -> VaultAddEditState.ViewState.Loading is VaultAddEditType.CloneItem -> VaultAddEditState.ViewState.Loading
}, },

View file

@ -0,0 +1,31 @@
package com.x8bit.bitwarden.ui.vault.feature.addedit.util
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
import com.x8bit.bitwarden.ui.platform.base.util.toHostOrPathOrNull
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
/**
* Returns pre-filled content that may be used for an "add" type
* [VaultAddEditState.ViewState.Content].
*/
fun AutofillSelectionData.toDefaultAddTypeContent(): VaultAddEditState.ViewState.Content {
val uri = this.uri
val simpleUri = uri?.toHostOrPathOrNull()
val defaultAddType = when (this.type) {
AutofillSelectionData.Type.CARD -> {
VaultAddEditState.ViewState.Content.ItemType.Card()
}
AutofillSelectionData.Type.LOGIN -> {
VaultAddEditState.ViewState.Content.ItemType.Login(
uri = uri.orEmpty(),
)
}
}
return VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(
name = simpleUri.orEmpty(),
),
type = defaultAddType,
)
}

View file

@ -7,7 +7,11 @@ import com.bitwarden.core.UriMatchType
import com.x8bit.bitwarden.R import com.x8bit.bitwarden.R
import com.x8bit.bitwarden.data.auth.repository.AuthRepository import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl
import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager import com.x8bit.bitwarden.data.platform.manager.clipboard.BitwardenClipboardManager
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
import com.x8bit.bitwarden.data.platform.repository.model.DataState import com.x8bit.bitwarden.data.platform.repository.model.DataState
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
@ -27,6 +31,7 @@ import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldAction
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType import com.x8bit.bitwarden.ui.vault.feature.addedit.model.CustomFieldType
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField import com.x8bit.bitwarden.ui.vault.feature.addedit.model.toCustomField
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toDefaultAddTypeContent
import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState import com.x8bit.bitwarden.ui.vault.feature.addedit.util.toViewState
import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType import com.x8bit.bitwarden.ui.vault.model.VaultAddEditType
import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand
@ -74,6 +79,8 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
every { getVaultItemStateFlow(DEFAULT_EDIT_ITEM_ID) } returns mutableVaultItemFlow every { getVaultItemStateFlow(DEFAULT_EDIT_ITEM_ID) } returns mutableVaultItemFlow
every { totpCodeFlow } returns totpTestCodeFlow every { totpCodeFlow } returns totpTestCodeFlow
} }
private val specialCircumstanceManager: SpecialCircumstanceManager =
SpecialCircumstanceManagerImpl()
private val generatorRepository: GeneratorRepository = FakeGeneratorRepository() private val generatorRepository: GeneratorRepository = FakeGeneratorRepository()
@ -107,7 +114,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
} }
@Test @Test
fun `initial add state should be correct`() = runTest { fun `initial add state should be correct when not autofill`() = runTest {
val vaultAddEditType = VaultAddEditType.AddItem val vaultAddEditType = VaultAddEditType.AddItem
val initState = createVaultAddItemState(vaultAddEditType = vaultAddEditType) val initState = createVaultAddItemState(vaultAddEditType = vaultAddEditType)
val viewModel = createAddVaultItemViewModel( val viewModel = createAddVaultItemViewModel(
@ -122,6 +129,35 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
} }
} }
@Test
fun `initial add state should be correct when autofill`() = runTest {
val autofillSelectionData = AutofillSelectionData(
type = AutofillSelectionData.Type.LOGIN,
uri = "https://www.test.com",
)
specialCircumstanceManager.specialCircumstance = SpecialCircumstance.AutofillSelection(
autofillSelectionData = autofillSelectionData,
shouldFinishWhenComplete = true,
)
val autofillContentState = autofillSelectionData.toDefaultAddTypeContent()
val vaultAddEditType = VaultAddEditType.AddItem
val initState = createVaultAddItemState(
vaultAddEditType = vaultAddEditType,
commonContentViewState = autofillContentState.common,
typeContentViewState = autofillContentState.type,
)
val viewModel = createAddVaultItemViewModel(
savedStateHandle = createSavedStateHandleWithState(
state = initState,
vaultAddEditType = vaultAddEditType,
),
)
assertEquals(initState, viewModel.stateFlow.value)
verify(exactly = 0) {
vaultRepository.getVaultItemStateFlow(DEFAULT_EDIT_ITEM_ID)
}
}
@Test @Test
fun `initial edit state should be correct`() = runTest { fun `initial edit state should be correct`() = runTest {
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID) val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
@ -1430,6 +1466,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
clipboardManager = clipboardManager, clipboardManager = clipboardManager,
vaultRepository = vaultRepository, vaultRepository = vaultRepository,
generatorRepository = generatorRepository, generatorRepository = generatorRepository,
specialCircumstanceManager = specialCircumstanceManager,
resourceManager = resourceManager, resourceManager = resourceManager,
authRepository = authRepository, authRepository = authRepository,
) )
@ -1950,6 +1987,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
clipboardManager = bitwardenClipboardManager, clipboardManager = bitwardenClipboardManager,
vaultRepository = vaultRepo, vaultRepository = vaultRepo,
generatorRepository = generatorRepo, generatorRepository = generatorRepo,
specialCircumstanceManager = specialCircumstanceManager,
resourceManager = bitwardenResourceManager, resourceManager = bitwardenResourceManager,
authRepository = authRepository, authRepository = authRepository,
) )

View file

@ -0,0 +1,42 @@
package com.x8bit.bitwarden.ui.vault.feature.addedit.util
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class AutofillSelectionDataExtensionsTest {
@Test
fun `toDefaultAddTypeContent for a Card type should return the correct Content`() {
assertEquals(
VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(),
type = VaultAddEditState.ViewState.Content.ItemType.Card(),
),
AutofillSelectionData(
type = AutofillSelectionData.Type.CARD,
uri = null,
)
.toDefaultAddTypeContent(),
)
}
@Test
fun `toDefaultAddTypeContent for a Login type should return the correct Content`() {
assertEquals(
VaultAddEditState.ViewState.Content(
common = VaultAddEditState.ViewState.Content.Common(
name = "www.test.com",
),
type = VaultAddEditState.ViewState.Content.ItemType.Login(
uri = "https://www.test.com",
),
),
AutofillSelectionData(
type = AutofillSelectionData.Type.LOGIN,
uri = "https://www.test.com",
)
.toDefaultAddTypeContent(),
)
}
}