mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
Add initial attachments screen shell (#745)
This commit is contained in:
parent
30ab22f826
commit
96201fd34c
15 changed files with 454 additions and 8 deletions
|
@ -24,6 +24,8 @@ import com.x8bit.bitwarden.ui.tools.feature.send.addsend.model.AddSendType
|
|||
import com.x8bit.bitwarden.ui.tools.feature.send.addsend.navigateToAddSend
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.navigateToVaultAddEdit
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.vaultAddEditDestination
|
||||
import com.x8bit.bitwarden.ui.vault.feature.attachments.attachmentDestination
|
||||
import com.x8bit.bitwarden.ui.vault.feature.attachments.navigateToAttachment
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.navigateToVaultItem
|
||||
import com.x8bit.bitwarden.ui.vault.feature.item.vaultItemDestination
|
||||
import com.x8bit.bitwarden.ui.vault.feature.manualcodeentry.navigateToManualCodeEntryScreen
|
||||
|
@ -90,6 +92,7 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
|||
},
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onNavigateToGeneratorModal = { navController.navigateToGeneratorModal(mode = it) },
|
||||
onNavigateToAttachments = { navController.navigateToAttachment(it) },
|
||||
)
|
||||
vaultMoveToOrganizationDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
|
@ -108,6 +111,7 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
|||
onNavigateToMoveToOrganization = {
|
||||
navController.navigateToVaultMoveToOrganization(it)
|
||||
},
|
||||
onNavigateToAttachments = { navController.navigateToAttachment(it) },
|
||||
)
|
||||
vaultQrCodeScanDestination(
|
||||
onNavigateToManualCodeEntryScreen = {
|
||||
|
@ -136,6 +140,9 @@ fun NavGraphBuilder.vaultUnlockedGraph(
|
|||
},
|
||||
onNavigateToViewCipher = { navController.navigateToVaultItem(it) },
|
||||
)
|
||||
attachmentDestination(
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ fun NavGraphBuilder.vaultAddEditDestination(
|
|||
onNavigateToManualCodeEntryScreen: () -> Unit,
|
||||
onNavigateToQrCodeScanScreen: () -> Unit,
|
||||
onNavigateToGeneratorModal: (GeneratorMode.Modal) -> Unit,
|
||||
onNavigateToAttachments: (cipherId: String) -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = ADD_EDIT_ITEM_ROUTE,
|
||||
|
@ -59,6 +60,7 @@ fun NavGraphBuilder.vaultAddEditDestination(
|
|||
onNavigateToManualCodeEntryScreen = onNavigateToManualCodeEntryScreen,
|
||||
onNavigateToQrCodeScanScreen = onNavigateToQrCodeScanScreen,
|
||||
onNavigateToGeneratorModal = onNavigateToGeneratorModal,
|
||||
onNavigateToAttachments = onNavigateToAttachments,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,12 +25,15 @@ import com.x8bit.bitwarden.ui.platform.components.BitwardenBasicDialog
|
|||
import com.x8bit.bitwarden.ui.platform.components.BitwardenErrorContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingContent
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenLoadingDialog
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenOverflowActionItem
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.LoadingDialogState
|
||||
import com.x8bit.bitwarden.ui.platform.components.OverflowMenuItemData
|
||||
import com.x8bit.bitwarden.ui.platform.manager.permissions.PermissionsManager
|
||||
import com.x8bit.bitwarden.ui.platform.theme.LocalPermissionsManager
|
||||
import com.x8bit.bitwarden.ui.platform.util.persistentListOfNotNull
|
||||
import com.x8bit.bitwarden.ui.tools.feature.generator.model.GeneratorMode
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCardTypeHandlers
|
||||
import com.x8bit.bitwarden.ui.vault.feature.addedit.handlers.VaultAddEditCommonHandlers
|
||||
|
@ -50,6 +53,7 @@ fun VaultAddEditScreen(
|
|||
permissionsManager: PermissionsManager = LocalPermissionsManager.current,
|
||||
onNavigateToManualCodeEntryScreen: () -> Unit,
|
||||
onNavigateToGeneratorModal: (GeneratorMode.Modal) -> Unit,
|
||||
onNavigateToAttachments: (cipherId: String) -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
val context = LocalContext.current
|
||||
|
@ -73,6 +77,8 @@ fun VaultAddEditScreen(
|
|||
Toast.makeText(context, event.message(resources), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
is VaultAddEditEvent.NavigateToAttachments -> onNavigateToAttachments(event.cipherId)
|
||||
|
||||
VaultAddEditEvent.NavigateBack -> onNavigateBack.invoke()
|
||||
}
|
||||
}
|
||||
|
@ -121,6 +127,21 @@ fun VaultAddEditScreen(
|
|||
{ viewModel.trySendAction(VaultAddEditAction.Common.SaveClick) }
|
||||
},
|
||||
)
|
||||
BitwardenOverflowActionItem(
|
||||
menuItemDataList = persistentListOfNotNull(
|
||||
OverflowMenuItemData(
|
||||
text = stringResource(id = R.string.attachments),
|
||||
onClick = remember(viewModel) {
|
||||
{
|
||||
viewModel.trySendAction(
|
||||
VaultAddEditAction.Common.AttachmentsClick,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
.takeUnless { state.isAddItemMode },
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
|
|
|
@ -141,6 +141,7 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
handleToggleMasterPasswordReprompt(action)
|
||||
}
|
||||
|
||||
is VaultAddEditAction.Common.AttachmentsClick -> handleAttachmentsClick()
|
||||
is VaultAddEditAction.Common.CloseClick -> handleCloseClick()
|
||||
is VaultAddEditAction.Common.DismissDialog -> handleDismissDialog()
|
||||
is VaultAddEditAction.Common.SaveClick -> handleSaveClick()
|
||||
|
@ -257,6 +258,10 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleAttachmentsClick() {
|
||||
onEdit { sendEvent(VaultAddEditEvent.NavigateToAttachments(it.vaultItemId)) }
|
||||
}
|
||||
|
||||
private fun handleCloseClick() {
|
||||
sendEvent(
|
||||
event = VaultAddEditEvent.NavigateBack,
|
||||
|
@ -1007,6 +1012,12 @@ class VaultAddEditViewModel @Inject constructor(
|
|||
(state.viewState as? VaultAddEditState.ViewState.Content)?.let(block)
|
||||
}
|
||||
|
||||
private inline fun onEdit(
|
||||
crossinline block: (VaultAddEditType.EditItem) -> Unit,
|
||||
) {
|
||||
(state.vaultAddEditType as? VaultAddEditType.EditItem)?.let(block)
|
||||
}
|
||||
|
||||
private inline fun updateContent(
|
||||
crossinline block: (
|
||||
VaultAddEditState.ViewState.Content,
|
||||
|
@ -1424,6 +1435,13 @@ sealed class VaultAddEditEvent {
|
|||
*/
|
||||
data object NavigateBack : VaultAddEditEvent()
|
||||
|
||||
/**
|
||||
* Navigate to attachments screen.
|
||||
*/
|
||||
data class NavigateToAttachments(
|
||||
val cipherId: String,
|
||||
) : VaultAddEditEvent()
|
||||
|
||||
/**
|
||||
* Navigate to the QR code scan screen.
|
||||
*/
|
||||
|
@ -1468,6 +1486,11 @@ sealed class VaultAddEditAction {
|
|||
*/
|
||||
data object DismissDialog : Common()
|
||||
|
||||
/**
|
||||
* The user has clicked the attachments overflow option.
|
||||
*/
|
||||
data object AttachmentsClick : Common()
|
||||
|
||||
/**
|
||||
* Represents the action when a type option is selected.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.attachments
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.navArgument
|
||||
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.composableWithSlideTransitions
|
||||
|
||||
private const val ATTACHMENTS_CIPHER_ID = "cipher_id"
|
||||
private const val ATTACHMENTS_ROUTE_PREFIX = "attachments"
|
||||
private const val ATTACHMENTS_ROUTE = "$ATTACHMENTS_ROUTE_PREFIX/{$ATTACHMENTS_CIPHER_ID}"
|
||||
|
||||
/**
|
||||
* Class to retrieve arguments from the [SavedStateHandle].
|
||||
*/
|
||||
@OmitFromCoverage
|
||||
data class AttachmentsArgs(val cipherId: String) {
|
||||
constructor(savedStateHandle: SavedStateHandle) : this(
|
||||
cipherId = checkNotNull(savedStateHandle.get<String>(ATTACHMENTS_CIPHER_ID)),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the attachments screen to the nav graph.
|
||||
*/
|
||||
fun NavGraphBuilder.attachmentDestination(
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = ATTACHMENTS_ROUTE,
|
||||
arguments = listOf(
|
||||
navArgument(ATTACHMENTS_CIPHER_ID) { type = NavType.StringType },
|
||||
),
|
||||
) {
|
||||
AttachmentsScreen(
|
||||
onNavigateBack = onNavigateBack,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the attachments screen.
|
||||
*/
|
||||
fun NavController.navigateToAttachment(
|
||||
cipherId: String,
|
||||
navOptions: NavOptions? = null,
|
||||
) {
|
||||
navigate(
|
||||
route = "$ATTACHMENTS_ROUTE_PREFIX/$cipherId",
|
||||
navOptions = navOptions,
|
||||
)
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.attachments
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.EventsEffect
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenScaffold
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTextButton
|
||||
import com.x8bit.bitwarden.ui.platform.components.BitwardenTopAppBar
|
||||
import com.x8bit.bitwarden.ui.platform.components.NavigationIcon
|
||||
import com.x8bit.bitwarden.ui.vault.feature.attachments.handlers.AttachmentsHandlers
|
||||
|
||||
/**
|
||||
* Displays the attachments screen.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AttachmentsScreen(
|
||||
viewModel: AttachmentsViewModel = hiltViewModel(),
|
||||
onNavigateBack: () -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
val context = LocalContext.current
|
||||
EventsEffect(viewModel = viewModel) { event ->
|
||||
when (event) {
|
||||
AttachmentsEvent.NavigateBack -> onNavigateBack()
|
||||
|
||||
is AttachmentsEvent.ShowToast -> {
|
||||
Toast
|
||||
.makeText(context, event.message(context.resources), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val attachmentHandlers = remember(viewModel) { AttachmentsHandlers.create(viewModel) }
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
BitwardenScaffold(
|
||||
modifier = Modifier
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.fillMaxSize(),
|
||||
topBar = {
|
||||
BitwardenTopAppBar(
|
||||
title = stringResource(id = R.string.attachments),
|
||||
scrollBehavior = scrollBehavior,
|
||||
navigationIcon = NavigationIcon(
|
||||
navigationIcon = painterResource(id = R.drawable.ic_back),
|
||||
navigationIconContentDescription = stringResource(id = R.string.back),
|
||||
onNavigationIconClick = attachmentHandlers.onBackClick,
|
||||
),
|
||||
actions = {
|
||||
BitwardenTextButton(
|
||||
label = stringResource(id = R.string.save),
|
||||
onClick = attachmentHandlers.onSaveClick,
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
) { innerPadding ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(innerPadding),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
item {
|
||||
Text(text = "Not Yet Implemented")
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.navigationBarsPadding())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.attachments
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
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 dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val KEY_STATE = "state"
|
||||
|
||||
/**
|
||||
* ViewModel responsible for handling user interactions in the attachments screen.
|
||||
*/
|
||||
@HiltViewModel
|
||||
class AttachmentsViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : BaseViewModel<AttachmentsState, AttachmentsEvent, AttachmentsAction>(
|
||||
// We load the state from the savedStateHandle for testing purposes.
|
||||
initialState = savedStateHandle[KEY_STATE]
|
||||
?: AttachmentsState(
|
||||
cipherId = AttachmentsArgs(savedStateHandle).cipherId,
|
||||
),
|
||||
) {
|
||||
override fun handleAction(action: AttachmentsAction) {
|
||||
when (action) {
|
||||
AttachmentsAction.BackClick -> handleBackClick()
|
||||
AttachmentsAction.SaveClick -> handleSaveClick()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleBackClick() {
|
||||
sendEvent(AttachmentsEvent.NavigateBack)
|
||||
}
|
||||
|
||||
private fun handleSaveClick() {
|
||||
sendEvent(AttachmentsEvent.ShowToast("Not Yet Implemented".asText()))
|
||||
// TODO: Handle saving the attachments (BIT-522)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the state for viewing attachments.
|
||||
*/
|
||||
@Parcelize
|
||||
data class AttachmentsState(
|
||||
val cipherId: String,
|
||||
) : Parcelable
|
||||
|
||||
/**
|
||||
* Represents a set of events related attachments.
|
||||
*/
|
||||
sealed class AttachmentsEvent {
|
||||
/**
|
||||
* Navigates back.
|
||||
*/
|
||||
data object NavigateBack : AttachmentsEvent()
|
||||
|
||||
/**
|
||||
* Displays the given [message] as a toast.
|
||||
*/
|
||||
data class ShowToast(
|
||||
val message: Text,
|
||||
) : AttachmentsEvent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a set of actions related to attachments.
|
||||
*/
|
||||
sealed class AttachmentsAction {
|
||||
/**
|
||||
* User clicked the back button.
|
||||
*/
|
||||
data object BackClick : AttachmentsAction()
|
||||
|
||||
/**
|
||||
* User clicked the save button.
|
||||
*/
|
||||
data object SaveClick : AttachmentsAction()
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.attachments.handlers
|
||||
|
||||
import com.x8bit.bitwarden.ui.vault.feature.attachments.AttachmentsAction
|
||||
import com.x8bit.bitwarden.ui.vault.feature.attachments.AttachmentsViewModel
|
||||
|
||||
/**
|
||||
* A collection of handler functions for managing actions within the context of viewing attachments.
|
||||
*/
|
||||
data class AttachmentsHandlers(
|
||||
val onBackClick: () -> Unit,
|
||||
val onSaveClick: () -> Unit,
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Creates the [AttachmentsHandlers] using the [AttachmentsViewModel] to send desired
|
||||
* actions.
|
||||
*/
|
||||
fun create(viewModel: AttachmentsViewModel): AttachmentsHandlers =
|
||||
AttachmentsHandlers(
|
||||
onBackClick = { viewModel.trySendAction(AttachmentsAction.BackClick) },
|
||||
onSaveClick = { viewModel.trySendAction(AttachmentsAction.SaveClick) },
|
||||
)
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ fun NavGraphBuilder.vaultItemDestination(
|
|||
onNavigateBack: () -> Unit,
|
||||
onNavigateToVaultEditItem: (vaultItemId: String, isClone: Boolean) -> Unit,
|
||||
onNavigateToMoveToOrganization: (vaultItemId: String) -> Unit,
|
||||
onNavigateToAttachments: (vaultItemId: String) -> Unit,
|
||||
) {
|
||||
composableWithSlideTransitions(
|
||||
route = VAULT_ITEM_ROUTE,
|
||||
|
@ -41,6 +42,7 @@ fun NavGraphBuilder.vaultItemDestination(
|
|||
onNavigateBack = onNavigateBack,
|
||||
onNavigateToVaultAddEditItem = onNavigateToVaultEditItem,
|
||||
onNavigateToMoveToOrganization = onNavigateToMoveToOrganization,
|
||||
onNavigateToAttachments = onNavigateToAttachments,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ fun VaultItemScreen(
|
|||
onNavigateBack: () -> Unit,
|
||||
onNavigateToVaultAddEditItem: (vaultItemId: String, isClone: Boolean) -> Unit,
|
||||
onNavigateToMoveToOrganization: (vaultItemId: String) -> Unit,
|
||||
onNavigateToAttachments: (vaultItemId: String) -> Unit,
|
||||
) {
|
||||
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
|
||||
val context = LocalContext.current
|
||||
|
@ -90,10 +91,7 @@ fun VaultItemScreen(
|
|||
|
||||
is VaultItemEvent.NavigateToUri -> intentManager.launchUri(event.uri.toUri())
|
||||
|
||||
is VaultItemEvent.NavigateToAttachments -> {
|
||||
// TODO implement attachments in BIT-522
|
||||
Toast.makeText(context, "Not yet implemented.", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
is VaultItemEvent.NavigateToAttachments -> onNavigateToAttachments(event.itemId)
|
||||
|
||||
is VaultItemEvent.NavigateToMoveToOrganization -> {
|
||||
onNavigateToMoveToOrganization(event.itemId)
|
||||
|
|
|
@ -60,6 +60,7 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
private var onNavigateQrCodeScanScreenCalled = false
|
||||
private var onNavigateToManualCodeEntryScreenCalled = false
|
||||
private var onNavigateToGeneratorModalType: GeneratorMode.Modal? = null
|
||||
private var onNavigateToAttachmentsId: String? = null
|
||||
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<VaultAddEditEvent>()
|
||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE_LOGIN)
|
||||
|
@ -76,13 +77,12 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
composeTestRule.setContent {
|
||||
VaultAddEditScreen(
|
||||
onNavigateBack = { onNavigateBackCalled = true },
|
||||
onNavigateToQrCodeScanScreen = {
|
||||
onNavigateQrCodeScanScreenCalled = true
|
||||
},
|
||||
onNavigateToQrCodeScanScreen = { onNavigateQrCodeScanScreenCalled = true },
|
||||
onNavigateToManualCodeEntryScreen = {
|
||||
onNavigateToManualCodeEntryScreenCalled = true
|
||||
},
|
||||
onNavigateToGeneratorModal = { onNavigateToGeneratorModalType = it },
|
||||
onNavigateToAttachments = { onNavigateToAttachmentsId = it },
|
||||
viewModel = viewModel,
|
||||
permissionsManager = fakePermissionManager,
|
||||
)
|
||||
|
@ -120,6 +120,13 @@ class VaultAddEditScreenTest : BaseComposeTest() {
|
|||
assertEquals(GeneratorMode.Modal.Password, onNavigateToGeneratorModalType)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on NavigateToAttachments event should invoke onNavigateToAttachments`() {
|
||||
val cipherId = "cipherId-1234"
|
||||
mutableEventFlow.tryEmit(VaultAddEditEvent.NavigateToAttachments(cipherId))
|
||||
assertEquals(cipherId, onNavigateToAttachmentsId)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `on NavigateToGeneratorModal event in username mode should invoke NavigateToGeneratorModal with Username Generator Mode `() {
|
||||
|
|
|
@ -161,6 +161,25 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AttachmentsClick should emit NavigateToAttachments`() = runTest {
|
||||
val vaultAddEditType = VaultAddEditType.EditItem(DEFAULT_EDIT_ITEM_ID)
|
||||
val initState = createVaultAddItemState(vaultAddEditType = vaultAddEditType)
|
||||
val viewModel = createAddVaultItemViewModel(
|
||||
savedStateHandle = createSavedStateHandleWithState(
|
||||
state = initState,
|
||||
vaultAddEditType = vaultAddEditType,
|
||||
),
|
||||
)
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.actionChannel.trySend(VaultAddEditAction.Common.AttachmentsClick)
|
||||
assertEquals(
|
||||
VaultAddEditEvent.NavigateToAttachments(DEFAULT_EDIT_ITEM_ID),
|
||||
awaitItem(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `in add mode, SaveClick should show dialog, and remove it once an item is saved`() =
|
||||
runTest {
|
||||
|
@ -1526,7 +1545,8 @@ class VaultAddEditViewModelTest : BaseViewModelTest() {
|
|||
"TestId 1",
|
||||
"Boolean Field",
|
||||
true,
|
||||
), VaultAddEditState.Custom.BooleanField(
|
||||
),
|
||||
VaultAddEditState.Custom.BooleanField(
|
||||
"TestId 3",
|
||||
"Boolean Field",
|
||||
true,
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.attachments
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseComposeTest
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
class AttachmentsScreenTest : BaseComposeTest() {
|
||||
private var onNavigateBackCalled = false
|
||||
|
||||
private val mutableStateFlow = MutableStateFlow(DEFAULT_STATE)
|
||||
private val mutableEventFlow = bufferedMutableSharedFlow<AttachmentsEvent>()
|
||||
val viewModel: AttachmentsViewModel = mockk {
|
||||
every { stateFlow } returns mutableStateFlow
|
||||
every { eventFlow } returns mutableEventFlow
|
||||
every { trySendAction(any()) } just runs
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
composeTestRule.setContent {
|
||||
AttachmentsScreen(
|
||||
viewModel = viewModel,
|
||||
onNavigateBack = { onNavigateBackCalled = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NavigateBack should call onNavigateBack`() {
|
||||
mutableEventFlow.tryEmit(AttachmentsEvent.NavigateBack)
|
||||
Assert.assertTrue(onNavigateBackCalled)
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_STATE: AttachmentsState = AttachmentsState(
|
||||
cipherId = "cipherId-1234",
|
||||
)
|
|
@ -0,0 +1,56 @@
|
|||
package com.x8bit.bitwarden.ui.vault.feature.attachments
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import app.cash.turbine.test
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseViewModelTest
|
||||
import com.x8bit.bitwarden.ui.platform.base.util.asText
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AttachmentsViewModelTest : BaseViewModelTest() {
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct when state is null`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
assertEquals(DEFAULT_STATE, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initial state should be correct when state is set`() = runTest {
|
||||
val initialState = DEFAULT_STATE.copy(cipherId = "123456789")
|
||||
val viewModel = createViewModel(initialState)
|
||||
assertEquals(initialState, viewModel.stateFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `BackClick should emit NavigateBack`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AttachmentsAction.BackClick)
|
||||
assertEquals(AttachmentsEvent.NavigateBack, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `SaveClick should emit ShowToast`() = runTest {
|
||||
val viewModel = createViewModel()
|
||||
viewModel.eventFlow.test {
|
||||
viewModel.trySendAction(AttachmentsAction.SaveClick)
|
||||
assertEquals(AttachmentsEvent.ShowToast("Not Yet Implemented".asText()), awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
private fun createViewModel(
|
||||
initialState: AttachmentsState? = null,
|
||||
): AttachmentsViewModel = AttachmentsViewModel(
|
||||
savedStateHandle = SavedStateHandle().apply {
|
||||
set("state", initialState)
|
||||
set("cipher_id", initialState?.cipherId ?: "cipherId-1234")
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private val DEFAULT_STATE: AttachmentsState = AttachmentsState(
|
||||
cipherId = "cipherId-1234",
|
||||
)
|
|
@ -57,6 +57,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
|||
private var onNavigateBackCalled = false
|
||||
private var onNavigateToVaultEditItemId: String? = null
|
||||
private var onNavigateToMoveToOrganizationItemId: String? = null
|
||||
private var onNavigateToAttachmentsId: String? = null
|
||||
|
||||
private val intentManager = mockk<IntentManager>()
|
||||
|
||||
|
@ -75,6 +76,7 @@ class VaultItemScreenTest : BaseComposeTest() {
|
|||
onNavigateBack = { onNavigateBackCalled = true },
|
||||
onNavigateToVaultAddEditItem = { id, _ -> onNavigateToVaultEditItemId = id },
|
||||
onNavigateToMoveToOrganization = { onNavigateToMoveToOrganizationItemId = it },
|
||||
onNavigateToAttachments = { onNavigateToAttachmentsId = it },
|
||||
intentManager = intentManager,
|
||||
)
|
||||
}
|
||||
|
@ -95,6 +97,13 @@ class VaultItemScreenTest : BaseComposeTest() {
|
|||
assertEquals(id, onNavigateToMoveToOrganizationItemId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NavigateToMoveToOrganization event should invoke onNavigateToAttachments`() {
|
||||
val id = "id1234"
|
||||
mutableEventFlow.tryEmit(VaultItemEvent.NavigateToAttachments(itemId = id))
|
||||
assertEquals(id, onNavigateToAttachmentsId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `on close click should send CloseClick`() {
|
||||
composeTestRule.onNodeWithContentDescription(label = "Close").performClick()
|
||||
|
|
Loading…
Reference in a new issue