mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 18:58:59 +03:00
BIT-1661: Pre-populate Add Item screen during autofill save (#913)
This commit is contained in:
parent
bafebb46f3
commit
4f08d5ddbe
8 changed files with 363 additions and 36 deletions
|
@ -1,8 +1,19 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager.util
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
|
||||
/**
|
||||
* Returns [AutofillSaveItem] when contained in the given [SpecialCircumstance].
|
||||
*/
|
||||
fun SpecialCircumstance.toAutofillSaveItemOrNull(): AutofillSaveItem? =
|
||||
when (this) {
|
||||
is SpecialCircumstance.AutofillSave -> this.autofillSaveItem
|
||||
is SpecialCircumstance.AutofillSelection -> null
|
||||
is SpecialCircumstance.ShareNewSend -> null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns [AutofillSelectionData] when contained in the given [SpecialCircumstance].
|
||||
*/
|
||||
|
|
|
@ -34,9 +34,12 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
|
|||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTwoButtonDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
|
||||
import com.x8bit.bitwarden.ui.platform.components.NavigationIcon
|
||||
import com.x8bit.bitwarden.ui.platform.components.OverflowMenuItemData
|
||||
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.LocalExitManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.LocalIntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.LocalPermissionsManager
|
||||
import com.x8bit.bitwarden.ui.platform.util.persistentListOfNotNull
|
||||
|
@ -58,6 +61,7 @@ fun VaultAddEditScreen(
|
|||
viewModel: VaultAddEditViewModel = hiltViewModel(),
|
||||
permissionsManager: PermissionsManager = LocalPermissionsManager.current,
|
||||
intentManager: IntentManager = LocalIntentManager.current,
|
||||
exitManager: ExitManager = LocalExitManager.current,
|
||||
onNavigateToManualCodeEntryScreen: () -> Unit,
|
||||
onNavigateToGeneratorModal: (GeneratorMode.Modal) -> Unit,
|
||||
onNavigateToAttachments: (cipherId: String) -> Unit,
|
||||
|
@ -94,6 +98,7 @@ fun VaultAddEditScreen(
|
|||
onNavigateToMoveToOrganization(event.cipherId, true)
|
||||
}
|
||||
|
||||
VaultAddEditEvent.ExitApp -> exitManager.exitApplication()
|
||||
VaultAddEditEvent.NavigateBack -> onNavigateBack.invoke()
|
||||
|
||||
is VaultAddEditEvent.NavigateToTooltipUri -> {
|
||||
|
@ -156,11 +161,14 @@ fun VaultAddEditScreen(
|
|||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = state.screenDisplayName(),
|
||||
navigationIcon = painterResource(id = R.drawable.ic_close),
|
||||
navigationIconContentDescription = stringResource(id = R.string.close),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultAddEditAction.Common.CloseClick) }
|
||||
},
|
||||
navigationIcon = NavigationIcon(
|
||||
navigationIcon = painterResource(id = R.drawable.ic_close),
|
||||
navigationIconContentDescription = stringResource(id = R.string.close),
|
||||
onNavigationIconClick = remember(viewModel) {
|
||||
{ viewModel.trySendAction(VaultAddEditAction.Common.CloseClick) }
|
||||
},
|
||||
)
|
||||
.takeIf { state.shouldShowCloseButton },
|
||||
scrollBehavior = scrollBehavior,
|
||||
actions = {
|
||||
BitwardenTextButton(
|
||||
|
|
|
@ -8,10 +8,10 @@ import com.x8bit.bitwarden.R
|
|||
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.UserState
|
||||
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.model.SpecialCircumstance
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSaveItemOrNull
|
||||
import com.x8bit.bitwarden.data.platform.manager.util.toAutofillSelectionDataOrNull
|
||||
import com.x8bit.bitwarden.data.platform.repository.model.DataState
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.takeUntilLoaded
|
||||
import com.x8bit.bitwarden.data.tools.generator.repository.GeneratorRepository
|
||||
|
@ -84,16 +84,16 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
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 autofillSaveItem = specialCircumstanceManager
|
||||
.specialCircumstance
|
||||
?.toAutofillSaveItemOrNull()
|
||||
val autofillSelectionData = specialCircumstanceManager
|
||||
.specialCircumstance
|
||||
?.toAutofillSelectionDataOrNull()
|
||||
val defaultAddTypeContent = autofillSelectionData
|
||||
?.toDefaultAddTypeContent()
|
||||
?: autofillSaveItem
|
||||
?.toDefaultAddTypeContent()
|
||||
?: VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Login(),
|
||||
|
@ -107,6 +107,9 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
is VaultAddEditType.CloneItem -> VaultAddEditState.ViewState.Loading
|
||||
},
|
||||
dialog = null,
|
||||
// Set special conditions for autofill save
|
||||
shouldShowCloseButton = autofillSaveItem == null,
|
||||
shouldExitOnSave = autofillSaveItem != null,
|
||||
)
|
||||
},
|
||||
) {
|
||||
|
@ -985,9 +988,16 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
is CreateCipherResult.Success -> {
|
||||
sendEvent(
|
||||
event = VaultAddEditEvent.NavigateBack,
|
||||
)
|
||||
if (state.shouldExitOnSave) {
|
||||
specialCircumstanceManager.specialCircumstance = null
|
||||
sendEvent(
|
||||
event = VaultAddEditEvent.ExitApp,
|
||||
)
|
||||
} else {
|
||||
sendEvent(
|
||||
event = VaultAddEditEvent.NavigateBack,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1064,6 +1074,10 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
DataState.Loading -> {
|
||||
// Skip loading states for add modes, since this will blow away any initial content
|
||||
// or user-selected content.
|
||||
if (state.isAddItemMode) return
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(viewState = VaultAddEditState.ViewState.Loading)
|
||||
}
|
||||
|
@ -1354,6 +1368,9 @@ data class VaultAddEditState(
|
|||
val vaultAddEditType: VaultAddEditType,
|
||||
val viewState: ViewState,
|
||||
val dialog: DialogState?,
|
||||
val shouldShowCloseButton: Boolean = true,
|
||||
// Internal
|
||||
val shouldExitOnSave: Boolean = false,
|
||||
) : Parcelable {
|
||||
|
||||
/**
|
||||
|
@ -1704,6 +1721,11 @@ sealed class VaultAddEditEvent {
|
|||
*/
|
||||
data class ShowToast(val message: Text) : VaultAddEditEvent()
|
||||
|
||||
/**
|
||||
* Leave the application.
|
||||
*/
|
||||
data object ExitApp : VaultAddEditEvent()
|
||||
|
||||
/**
|
||||
* Navigate back to previous screen.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.addedit.util
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.toHostOrPathOrNull
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Returns pre-filled content that may be used for an "add" type
|
||||
* [VaultAddEditState.ViewState.Content].
|
||||
*/
|
||||
fun AutofillSaveItem.toDefaultAddTypeContent(): VaultAddEditState.ViewState.Content =
|
||||
when (this) {
|
||||
is AutofillSaveItem.Card -> {
|
||||
VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Card(
|
||||
number = this.number.orEmpty(),
|
||||
expirationMonth = VaultCardExpirationMonth
|
||||
.entries
|
||||
.find { it.number == this.expirationMonth }
|
||||
?: VaultCardExpirationMonth.SELECT,
|
||||
expirationYear = this.expirationYear.orEmpty(),
|
||||
securityCode = this.securityCode.orEmpty(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
is AutofillSaveItem.Login -> {
|
||||
val uri = this.uri
|
||||
val simpleUri = uri?.toHostOrPathOrNull()
|
||||
VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
name = simpleUri.orEmpty(),
|
||||
),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Login(
|
||||
username = this.username.orEmpty(),
|
||||
password = this.password.orEmpty(),
|
||||
uriList = listOf(
|
||||
UriItem(
|
||||
id = UUID.randomUUID().toString(),
|
||||
uri = uri,
|
||||
match = null,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.platform.manager.util
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||
import com.x8bit.bitwarden.data.platform.manager.model.SpecialCircumstance
|
||||
import io.mockk.mockk
|
||||
|
@ -10,7 +11,37 @@ import org.junit.jupiter.api.Test
|
|||
class SpecialCircumstanceExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `toAutofillSelectionDataOrNull should a non-null value for AutofillSelection`() {
|
||||
fun `toAutofillSaveItemOrNull should return a non-null value for AutofillSave`() {
|
||||
val autofillSaveItem: AutofillSaveItem = mockk()
|
||||
assertEquals(
|
||||
autofillSaveItem,
|
||||
SpecialCircumstance
|
||||
.AutofillSave(
|
||||
autofillSaveItem = autofillSaveItem,
|
||||
)
|
||||
.toAutofillSaveItemOrNull(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toAutofillSaveItemOrNull should return a null value for other types`() {
|
||||
listOf(
|
||||
SpecialCircumstance.AutofillSelection(
|
||||
autofillSelectionData = mockk(),
|
||||
shouldFinishWhenComplete = true,
|
||||
),
|
||||
SpecialCircumstance.ShareNewSend(
|
||||
data = mockk(),
|
||||
shouldFinishWhenComplete = true,
|
||||
),
|
||||
)
|
||||
.forEach { specialCircumstance ->
|
||||
assertNull(specialCircumstance.toAutofillSaveItemOrNull())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toAutofillSelectionDataOrNull should return a non-null value for AutofillSelection`() {
|
||||
val autofillSelectionData = AutofillSelectionData(
|
||||
type = AutofillSelectionData.Type.LOGIN,
|
||||
uri = "uri",
|
||||
|
@ -27,7 +58,7 @@ class SpecialCircumstanceExtensionsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `toAutofillSelectionDataOrNull should a null value for other types`() {
|
||||
fun `toAutofillSelectionDataOrNull should return a null value for other types`() {
|
||||
listOf(
|
||||
SpecialCircumstance.AutofillSave(
|
||||
autofillSaveItem = mockk(),
|
||||
|
|
|
@ -35,6 +35,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFl
|
|||
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import com.x8bit.bitwarden.ui.platform.manager.exit.ExitManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.intent.IntentManager
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.FakePermissionManager
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
|
@ -82,7 +83,9 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
every { eventFlow } returns mutableEventFlow
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
}
|
||||
|
||||
private val exitManager: ExitManager = mockk {
|
||||
every { exitApplication() } just runs
|
||||
}
|
||||
private val intentManager: IntentManager = mockk {
|
||||
every { launchUri(any()) } just runs
|
||||
}
|
||||
|
@ -101,11 +104,18 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
onNavigateToMoveToOrganization = { id, _ -> onNavigateToMoveToOrganizationId = id },
|
||||
viewModel = viewModel,
|
||||
permissionsManager = fakePermissionManager,
|
||||
exitManager = exitManager,
|
||||
intentManager = intentManager,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on ExitApp event should call the exitApplication of ExitManager`() {
|
||||
mutableEventFlow.tryEmit(VaultAddEditEvent.ExitApp)
|
||||
verify { exitManager.exitApplication() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateBack event should invoke onNavigateBack`() {
|
||||
mutableEventFlow.tryEmit(VaultAddEditEvent.NavigateBack)
|
||||
|
@ -173,6 +183,17 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
assertEquals(GeneratorMode.Modal.Username, onNavigateToGeneratorModalType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `close button should update according to state`() {
|
||||
composeTestRule.onNodeWithContentDescription("Close").assertIsDisplayed()
|
||||
|
||||
mutableStateFlow.update {
|
||||
it.copy(shouldShowCloseButton = false)
|
||||
}
|
||||
|
||||
composeTestRule.onNodeWithContentDescription("Close").assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clicking close button should send CloseClick action`() {
|
||||
composeTestRule
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.x8bit.bitwarden.ui.vault.feature.addedit
|
|||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import app.cash.turbine.turbineScope
|
||||
import com.bitwarden.core.CipherView
|
||||
import com.bitwarden.core.CollectionView
|
||||
import com.bitwarden.core.FolderView
|
||||
|
@ -13,6 +14,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.BreachCountResult
|
|||
import com.x8bit.bitwarden.data.auth.repository.model.Organization
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.UserState
|
||||
import com.x8bit.bitwarden.data.auth.repository.model.VaultUnlockType
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSelectionData
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManagerImpl
|
||||
|
@ -59,6 +61,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
@ -119,7 +122,10 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
viewModel.stateFlow.test {
|
||||
assertEquals(
|
||||
loginInitialState.copy(viewState = VaultAddEditState.ViewState.Loading),
|
||||
createVaultAddItemState(
|
||||
commonContentViewState = VaultAddEditState.ViewState.Content.Common(),
|
||||
typeContentViewState = createLoginTypeContentViewState(),
|
||||
),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
|
@ -136,7 +142,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
),
|
||||
)
|
||||
assertEquals(
|
||||
initState.copy(viewState = VaultAddEditState.ViewState.Loading),
|
||||
initState,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
verify(exactly = 1) {
|
||||
|
@ -145,7 +151,7 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun `initial add state should be correct when autofill`() = runTest {
|
||||
fun `initial add state should be correct when autofill selection`() = runTest {
|
||||
val autofillSelectionData = AutofillSelectionData(
|
||||
type = AutofillSelectionData.Type.LOGIN,
|
||||
uri = "https://www.test.com",
|
||||
|
@ -168,7 +174,39 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
),
|
||||
)
|
||||
assertEquals(
|
||||
initState.copy(viewState = VaultAddEditState.ViewState.Loading),
|
||||
initState,
|
||||
viewModel.stateFlow.value,
|
||||
)
|
||||
verify(exactly = 1) {
|
||||
vaultRepository.vaultDataStateFlow
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial add state should be correct when autofill save`() = runTest {
|
||||
val autofillSaveItem = AutofillSaveItem.Login(
|
||||
username = "username",
|
||||
password = "password",
|
||||
uri = "https://www.test.com",
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance = SpecialCircumstance.AutofillSave(
|
||||
autofillSaveItem = autofillSaveItem,
|
||||
)
|
||||
val autofillContentState = autofillSaveItem.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 = 1) {
|
||||
|
@ -375,8 +413,9 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in add mode, SaveClick should show dialog, and remove it once an item is saved`() =
|
||||
fun `in add mode, SaveClick should show dialog, remove it once an item is saved, and emit NavigateBack`() =
|
||||
runTest {
|
||||
val stateWithDialog = createVaultAddItemState(
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
|
@ -387,36 +426,104 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
name = "mockName-1",
|
||||
),
|
||||
)
|
||||
|
||||
val stateWithName = createVaultAddItemState(
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
commonContentViewState = createCommonContentViewState(
|
||||
name = "mockName-1",
|
||||
),
|
||||
)
|
||||
|
||||
mutableVaultDataFlow.value = DataState.Loaded(
|
||||
createVaultData(),
|
||||
)
|
||||
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
createSavedStateHandleWithState(
|
||||
state = stateWithName,
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
),
|
||||
)
|
||||
|
||||
coEvery {
|
||||
vaultRepository.createCipherInOrganization(any(), any())
|
||||
} returns CreateCipherResult.Success
|
||||
|
||||
viewModel.stateFlow.test {
|
||||
viewModel.actionChannel.trySend(VaultAddEditAction.Common.SaveClick)
|
||||
assertEquals(stateWithName, awaitItem())
|
||||
assertEquals(stateWithDialog, awaitItem())
|
||||
assertEquals(stateWithName, awaitItem())
|
||||
}
|
||||
turbineScope {
|
||||
val stateTurbine = viewModel.stateFlow.testIn(backgroundScope)
|
||||
val eventTurbine = viewModel.eventFlow.testIn(backgroundScope)
|
||||
|
||||
viewModel.actionChannel.trySend(VaultAddEditAction.Common.SaveClick)
|
||||
|
||||
assertEquals(stateWithName, stateTurbine.awaitItem())
|
||||
assertEquals(stateWithDialog, stateTurbine.awaitItem())
|
||||
assertEquals(stateWithName, stateTurbine.awaitItem())
|
||||
|
||||
assertEquals(
|
||||
VaultAddEditEvent.NavigateBack,
|
||||
eventTurbine.awaitItem(),
|
||||
)
|
||||
}
|
||||
coVerify(exactly = 1) {
|
||||
vaultRepository.createCipherInOrganization(any(), any())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `in add mode during autofill, SaveClick should show dialog, remove it once an item is saved, and emit ExitApp`() =
|
||||
runTest {
|
||||
val autofillSaveItem = AutofillSaveItem.Login(
|
||||
username = null,
|
||||
password = null,
|
||||
uri = null,
|
||||
)
|
||||
specialCircumstanceManager.specialCircumstance =
|
||||
SpecialCircumstance.AutofillSave(
|
||||
autofillSaveItem = autofillSaveItem,
|
||||
)
|
||||
val stateWithDialog = createVaultAddItemState(
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
dialogState = VaultAddEditState.DialogState.Loading(
|
||||
R.string.saving.asText(),
|
||||
),
|
||||
commonContentViewState = createCommonContentViewState(
|
||||
name = "mockName-1",
|
||||
),
|
||||
)
|
||||
.copy(shouldExitOnSave = true)
|
||||
val stateWithName = createVaultAddItemState(
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
commonContentViewState = createCommonContentViewState(
|
||||
name = "mockName-1",
|
||||
),
|
||||
)
|
||||
.copy(shouldExitOnSave = true)
|
||||
mutableVaultDataFlow.value = DataState.Loaded(
|
||||
createVaultData(),
|
||||
)
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
createSavedStateHandleWithState(
|
||||
state = stateWithName,
|
||||
vaultAddEditType = VaultAddEditType.AddItem,
|
||||
),
|
||||
)
|
||||
coEvery {
|
||||
vaultRepository.createCipherInOrganization(any(), any())
|
||||
} returns CreateCipherResult.Success
|
||||
|
||||
turbineScope {
|
||||
val stateTurbine = viewModel.stateFlow.testIn(backgroundScope)
|
||||
val eventTurbine = viewModel.eventFlow.testIn(backgroundScope)
|
||||
|
||||
viewModel.actionChannel.trySend(VaultAddEditAction.Common.SaveClick)
|
||||
|
||||
assertEquals(stateWithName, stateTurbine.awaitItem())
|
||||
assertEquals(stateWithDialog, stateTurbine.awaitItem())
|
||||
assertEquals(stateWithName, stateTurbine.awaitItem())
|
||||
|
||||
assertEquals(
|
||||
VaultAddEditEvent.ExitApp,
|
||||
eventTurbine.awaitItem(),
|
||||
)
|
||||
}
|
||||
assertNull(specialCircumstanceManager.specialCircumstance)
|
||||
coVerify(exactly = 1) {
|
||||
vaultRepository.createCipherInOrganization(any(), any())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.addedit.util
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillSaveItem
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.VaultAddEditState
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.model.UriItem
|
||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardExpirationMonth
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.util.UUID
|
||||
|
||||
class AutofillSaveItemExtensionsTest {
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
mockkStatic(UUID::randomUUID)
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun tearDown() {
|
||||
unmockkStatic(UUID::randomUUID)
|
||||
}
|
||||
|
||||
@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(
|
||||
number = "number",
|
||||
expirationMonth = VaultCardExpirationMonth.JANUARY,
|
||||
expirationYear = "2024",
|
||||
securityCode = "securityCode",
|
||||
),
|
||||
),
|
||||
AutofillSaveItem.Card(
|
||||
number = "number",
|
||||
expirationMonth = "1",
|
||||
expirationYear = "2024",
|
||||
securityCode = "securityCode",
|
||||
)
|
||||
.toDefaultAddTypeContent(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toDefaultAddTypeContent for a Login type should return the correct Content`() {
|
||||
every { UUID.randomUUID().toString() } returns "uuid"
|
||||
assertEquals(
|
||||
VaultAddEditState.ViewState.Content(
|
||||
common = VaultAddEditState.ViewState.Content.Common(
|
||||
name = "www.test.com",
|
||||
),
|
||||
type = VaultAddEditState.ViewState.Content.ItemType.Login(
|
||||
username = "username",
|
||||
password = "password",
|
||||
uriList = listOf(
|
||||
UriItem(
|
||||
id = "uuid",
|
||||
uri = "https://www.test.com",
|
||||
match = null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
AutofillSaveItem.Login(
|
||||
username = "username",
|
||||
password = "password",
|
||||
uri = "https://www.test.com",
|
||||
)
|
||||
.toDefaultAddTypeContent(),
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue